From e565c19438a22be4d88e3b2f3a3744d70c1a0e51 Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 15 Jul 2015 09:13:38 -0300 Subject: [PATCH 01/26] Normalized line endings --- .../blah/concurrency/first/Producer.java | 84 ++-- .../blah/concurrency/second/Cursor.java | 58 +-- .../source/blah/concurrency/second/Data.java | 56 +-- .../second/DefaultJobDispatcher.java | 52 +-- .../concurrency/second/FieldReference.java | 114 ++--- .../source/blah/concurrency/second/Job.java | 14 +- .../concurrency/second/JobDispatcher.java | 12 +- .../blah/concurrency/second/JobQueue.java | 134 +++--- .../blah/concurrency/second/JobRunner.java | 72 +-- .../blah/concurrency/second/Locker.java | 36 +- .../source/blah/concurrency/third/Server.java | 12 +- .../FirstConsumerBenchmarkTest.java | 96 ++-- .../tests/blah/concurrency/JobQueueTest.java | 112 ++--- .../SecondConsumerBenchmarkTest.java | 100 ++-- .../blah/concurrency/TestDefinitions.java | 16 +- .../ThirdConsumerBenchmarkTest.java | 42 +- .../blah/concurrency/helpers/ConsumerJob.java | 36 +- .../blah/concurrency/helpers/EmptyJob.java | 32 +- .../concurrency/helpers/LateConsumer.java | 42 +- .../tests/blah/concurrency/helpers/Timer.java | 38 +- .../spi/GeneratedFromStatelessService.java | 26 +- .../source/trip/spi/ProducerFactory.java | 14 +- trip-core/source/trip/spi/Provided.java | 46 +- .../source/trip/spi/ProvidedServices.java | 66 +-- .../source/trip/spi/ProviderContext.java | 90 ++-- .../source/trip/spi/ServiceProvider.java | 436 +++++++++--------- .../trip/spi/ServiceProviderException.java | 28 +- trip-core/source/trip/spi/Singleton.java | 44 +- trip-core/source/trip/spi/Stateless.java | 40 +- .../spi/helpers/EmptyProviderContext.java | 56 +-- .../spi/helpers/FieldProviderContext.java | 80 ++-- .../spi/helpers/KeyValueProviderContext.java | 106 ++--- .../trip/spi/helpers/ProducerFactoryMap.java | 110 ++--- .../trip/spi/helpers/ProvidableClass.java | 98 ++-- .../helpers/SingleElementProvidableField.java | 120 ++--- .../spi/helpers/SingleObjectIterable.java | 80 ++-- .../spi/helpers/cache/CachedIterable.java | 64 +-- .../spi/helpers/cache/LazyClassInstantor.java | 88 ++-- .../spi/helpers/cache/LazyClassReader.java | 228 ++++----- .../trip/spi/helpers/cache/ServiceLoader.java | 52 +-- .../trip/spi/helpers/filter/AnyObject.java | 24 +- .../trip/spi/helpers/filter/Condition.java | 10 +- .../trip/spi/helpers/filter/Filter.java | 48 +- .../trip/spi/helpers/filter/NamedClass.java | 28 +- .../trip/spi/helpers/filter/NamedObject.java | 32 +- .../META-INF/services/trip.spi.Printable | 2 +- trip-core/tests/trip/spi/Printable.java | 12 +- trip-core/tests/trip/spi/PrintableHello.java | 30 +- trip-core/tests/trip/spi/PrintableWord.java | 12 +- trip-core/tests/trip/spi/PrintableWorld.java | 30 +- trip-core/tests/trip/spi/ProviderTest.java | 98 ++-- .../trip/spi/SuperclassInjectionTest.java | 72 +-- .../source/trip/spi/tests/Batman.java | 32 +- .../source/trip/spi/tests/HelloFoo.java | 18 +- .../trip/spi/tests/ProducerOfShorts.java | 14 +- .../trip/spi/tests/SerializableBean.java | 16 +- .../SingletonProvidedProducerOfIntegers.java | 34 +- .../StatelessProvidedProducerOfShorts.java | 40 +- ...nOfServiceDefinedByItsExposedTypeTest.java | 64 +-- .../SingletonsAndStatelessProducerTest.java | 106 ++--- .../source/META-INF/stateless-class.mustache | 120 ++--- .../trip/spi/inject/NameTransformations.java | 16 +- .../tests/stateless-class-exposing-class.txt | 106 ++--- .../stateless-class-exposing-interface.txt | 106 ++--- 64 files changed, 2050 insertions(+), 2050 deletions(-) diff --git a/sample/source/blah/concurrency/first/Producer.java b/sample/source/blah/concurrency/first/Producer.java index c3a4d89..6c1f24f 100644 --- a/sample/source/blah/concurrency/first/Producer.java +++ b/sample/source/blah/concurrency/first/Producer.java @@ -1,42 +1,42 @@ -package blah.concurrency.first; - -import java.util.concurrent.BlockingQueue; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.LinkedBlockingQueue; -import java.util.concurrent.TimeUnit; - -import lombok.RequiredArgsConstructor; - -@RequiredArgsConstructor -public class Producer { - - final ExecutorService executor = Executors.newCachedThreadPool(); - final BlockingQueue data = new LinkedBlockingQueue(); - final CountDownLatch counter; - - public Consumer createConsumer() { - final Consumer consumer = new Consumer(data, counter); - executor.submit(consumer); - return consumer; - } - - public void createConsumers( final int amountOfConsumers ) { - for ( int i=0; i data = new LinkedBlockingQueue(); + final CountDownLatch counter; + + public Consumer createConsumer() { + final Consumer consumer = new Consumer(data, counter); + executor.submit(consumer); + return consumer; + } + + public void createConsumers( final int amountOfConsumers ) { + for ( int i=0; i { - - final FieldReference wroteField = FieldReference.wrap( this, "wrote" ); -// final Locker readLocker = new Locker(); -// final Locker writeLocker = new Locker(); - - volatile T value; - volatile int wrote = 0; - - public T get() { - while( true ) - if ( wroteField.compareAndSet( 1, 0 ) ) - return currentValue(); - } - - public T set(final T data) { - while( true ) - if ( wroteField.compareAndSet( 0, 1 ) ) - return this.value = data; - } - - T currentValue() { - return value; - } -} +package blah.concurrency.second; + + +public class Data { + + final FieldReference wroteField = FieldReference.wrap( this, "wrote" ); +// final Locker readLocker = new Locker(); +// final Locker writeLocker = new Locker(); + + volatile T value; + volatile int wrote = 0; + + public T get() { + while( true ) + if ( wroteField.compareAndSet( 1, 0 ) ) + return currentValue(); + } + + public T set(final T data) { + while( true ) + if ( wroteField.compareAndSet( 0, 1 ) ) + return this.value = data; + } + + T currentValue() { + return value; + } +} diff --git a/sample/source/blah/concurrency/second/DefaultJobDispatcher.java b/sample/source/blah/concurrency/second/DefaultJobDispatcher.java index 0abc1d2..22a8692 100644 --- a/sample/source/blah/concurrency/second/DefaultJobDispatcher.java +++ b/sample/source/blah/concurrency/second/DefaultJobDispatcher.java @@ -1,26 +1,26 @@ -package blah.concurrency.second; - -import java.util.concurrent.Executors; - -public class DefaultJobDispatcher implements JobDispatcher { - - final int availableProcessors = Runtime.getRuntime().availableProcessors(); - final java.util.concurrent.ExecutorService executor; - final JobQueue buffer; - - public DefaultJobDispatcher( int bufferSize ) { - buffer = new JobQueue(bufferSize); - executor = Executors.newFixedThreadPool( availableProcessors ); - initializeJobRunners(); - } - - void initializeJobRunners(){ - for ( int i=0; i clazz, String fieldName ) { - try { - return unsafe.objectFieldOffset - ( clazz.getDeclaredField( fieldName )); - } catch (Exception ex) { throw new Error(ex); } - } - - /** - * Atomically sets the value to the given updated value - * if the current value {@code ==} the expected value. - * - * @param expect the expected value - * @param update the new value - * @return true if successful. False return indicates that - * the actual value was not equal to the expected value. - */ - public final boolean compareAndSet(int expect, int update) { - return unsafe.compareAndSwapInt(target, fieldAddress, expect, update); - } - - public final boolean compareAndSet(T expect, T update) { - return unsafe.compareAndSwapObject(target, fieldAddress, expect, update); - } -} +package blah.concurrency.second; + +import java.lang.reflect.Field; + +import lombok.RequiredArgsConstructor; +import sun.misc.Unsafe; + +/** + * A simple CAS abstraction using {@link Unsafe} class. + */ +@SuppressWarnings("restriction") +@RequiredArgsConstructor +public class FieldReference { + + static final Unsafe unsafe = getUnsafe(); + final Object target; + final long fieldAddress; + + public static Unsafe getUnsafe() { + try { + Field f = Unsafe.class.getDeclaredField("theUnsafe"); + f.setAccessible(true); + return (Unsafe)f.get(null); + } catch (Exception e) { + throw new Error(e); + } + } + + public static FieldReference wrap( Object object, String fieldName ) { + long fieldAddress = getFieldAddress(object.getClass(), fieldName); + return new FieldReference(object, fieldAddress); + } + + public static long getFieldAddress( Class clazz, String fieldName ) { + try { + return unsafe.objectFieldOffset + ( clazz.getDeclaredField( fieldName )); + } catch (Exception ex) { throw new Error(ex); } + } + + /** + * Atomically sets the value to the given updated value + * if the current value {@code ==} the expected value. + * + * @param expect the expected value + * @param update the new value + * @return true if successful. False return indicates that + * the actual value was not equal to the expected value. + */ + public final boolean compareAndSet(int expect, int update) { + return unsafe.compareAndSwapInt(target, fieldAddress, expect, update); + } + + public final boolean compareAndSet(T expect, T update) { + return unsafe.compareAndSwapObject(target, fieldAddress, expect, update); + } +} diff --git a/sample/source/blah/concurrency/second/Job.java b/sample/source/blah/concurrency/second/Job.java index 715611a..c7bfa22 100644 --- a/sample/source/blah/concurrency/second/Job.java +++ b/sample/source/blah/concurrency/second/Job.java @@ -1,7 +1,7 @@ -package blah.concurrency.second; - -public interface Job { - - void run( JobDispatcher executor ) throws InterruptedException; - -} +package blah.concurrency.second; + +public interface Job { + + void run( JobDispatcher executor ) throws InterruptedException; + +} diff --git a/sample/source/blah/concurrency/second/JobDispatcher.java b/sample/source/blah/concurrency/second/JobDispatcher.java index bc55734..af3fdc5 100644 --- a/sample/source/blah/concurrency/second/JobDispatcher.java +++ b/sample/source/blah/concurrency/second/JobDispatcher.java @@ -1,7 +1,7 @@ -package blah.concurrency.second; - -public interface JobDispatcher { - - void submit(Job job) throws InterruptedException; - +package blah.concurrency.second; + +public interface JobDispatcher { + + void submit(Job job) throws InterruptedException; + } \ No newline at end of file diff --git a/sample/source/blah/concurrency/second/JobQueue.java b/sample/source/blah/concurrency/second/JobQueue.java index 80f8fc2..1a34166 100644 --- a/sample/source/blah/concurrency/second/JobQueue.java +++ b/sample/source/blah/concurrency/second/JobQueue.java @@ -1,67 +1,67 @@ -package blah.concurrency.second; - -import java.util.concurrent.atomic.AtomicReference; -import java.util.concurrent.locks.LockSupport; - -import lombok.val; - -public class JobQueue { - - final Locker emptySlotsLocker = new Locker(); - final Locker fullSlotsLocker = new Locker(); - - final AtomicReference[] jobs; - final Cursor readCursor; - final Cursor writeCursor; - final int bufferSize; - - volatile int wroteData = 0; - - @SuppressWarnings("unchecked") - public JobQueue( final int bufferSize ) { - this.jobs = new AtomicReference[ bufferSize ]; - this.readCursor = new Cursor(bufferSize); - this.writeCursor = new Cursor(bufferSize); - this.bufferSize = bufferSize; - preAllocateBuffer(); - } - - void preAllocateBuffer() { - for ( int i=0; i(); - } - - public void put( final Job job ) throws InterruptedException { - val writablePosition = writeCursor.next(); - final AtomicReference reference = this.jobs[ writablePosition ]; - while( !reference.compareAndSet(null, job)) - LockSupport.parkNanos(reference, 1l); - wroteData++; - } - - int waitUntilHaveWritableSlots() { - return writeCursor.next(); - } - - boolean haveWritableSlots() { - return this.wroteData <= bufferSize; - } - - public Job get() throws InterruptedException { - val readableSlot = waitUntilHaveReadableSlots(); - final Job job = readableSlot.getAndSet(null); - this.wroteData--; - return job; - } - - AtomicReference waitUntilHaveReadableSlots() { - final AtomicReference reference = this.jobs[ readCursor.next() ]; - while( reference.get() == null ) - LockSupport.parkNanos(reference, 1l); - return reference; - } - - boolean haveReadbleData() { - return this.wroteData > 0; - } -} +package blah.concurrency.second; + +import java.util.concurrent.atomic.AtomicReference; +import java.util.concurrent.locks.LockSupport; + +import lombok.val; + +public class JobQueue { + + final Locker emptySlotsLocker = new Locker(); + final Locker fullSlotsLocker = new Locker(); + + final AtomicReference[] jobs; + final Cursor readCursor; + final Cursor writeCursor; + final int bufferSize; + + volatile int wroteData = 0; + + @SuppressWarnings("unchecked") + public JobQueue( final int bufferSize ) { + this.jobs = new AtomicReference[ bufferSize ]; + this.readCursor = new Cursor(bufferSize); + this.writeCursor = new Cursor(bufferSize); + this.bufferSize = bufferSize; + preAllocateBuffer(); + } + + void preAllocateBuffer() { + for ( int i=0; i(); + } + + public void put( final Job job ) throws InterruptedException { + val writablePosition = writeCursor.next(); + final AtomicReference reference = this.jobs[ writablePosition ]; + while( !reference.compareAndSet(null, job)) + LockSupport.parkNanos(reference, 1l); + wroteData++; + } + + int waitUntilHaveWritableSlots() { + return writeCursor.next(); + } + + boolean haveWritableSlots() { + return this.wroteData <= bufferSize; + } + + public Job get() throws InterruptedException { + val readableSlot = waitUntilHaveReadableSlots(); + final Job job = readableSlot.getAndSet(null); + this.wroteData--; + return job; + } + + AtomicReference waitUntilHaveReadableSlots() { + final AtomicReference reference = this.jobs[ readCursor.next() ]; + while( reference.get() == null ) + LockSupport.parkNanos(reference, 1l); + return reference; + } + + boolean haveReadbleData() { + return this.wroteData > 0; + } +} diff --git a/sample/source/blah/concurrency/second/JobRunner.java b/sample/source/blah/concurrency/second/JobRunner.java index 926d0de..e0afbba 100644 --- a/sample/source/blah/concurrency/second/JobRunner.java +++ b/sample/source/blah/concurrency/second/JobRunner.java @@ -1,36 +1,36 @@ -package blah.concurrency.second; - -import lombok.RequiredArgsConstructor; - -@RequiredArgsConstructor -public class JobRunner implements Runnable { - - final JobQueue buffer; - final JobDispatcher dispatcher; - - Job currentJob; - - @Override - public void run() { - while( true ) - if ( hasMoreJobsToRun() ) - tryRunCurrentJob(); - } - - void tryRunCurrentJob() { - try { - currentJob.run( dispatcher ); - } catch (InterruptedException e) { - e.printStackTrace(); - } - } - - boolean hasMoreJobsToRun(){ - try { - currentJob = buffer.get(); - return currentJob != null; - } catch (InterruptedException e) { - return false; - } - } -} +package blah.concurrency.second; + +import lombok.RequiredArgsConstructor; + +@RequiredArgsConstructor +public class JobRunner implements Runnable { + + final JobQueue buffer; + final JobDispatcher dispatcher; + + Job currentJob; + + @Override + public void run() { + while( true ) + if ( hasMoreJobsToRun() ) + tryRunCurrentJob(); + } + + void tryRunCurrentJob() { + try { + currentJob.run( dispatcher ); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + + boolean hasMoreJobsToRun(){ + try { + currentJob = buffer.get(); + return currentJob != null; + } catch (InterruptedException e) { + return false; + } + } +} diff --git a/sample/source/blah/concurrency/second/Locker.java b/sample/source/blah/concurrency/second/Locker.java index 2cfbda2..48a5a02 100644 --- a/sample/source/blah/concurrency/second/Locker.java +++ b/sample/source/blah/concurrency/second/Locker.java @@ -1,18 +1,18 @@ -package blah.concurrency.second; - -import java.util.concurrent.locks.Condition; -import java.util.concurrent.locks.ReentrantLock; - -public class Locker { - - final ReentrantLock lock = new ReentrantLock(); - final Condition waitCondition = lock.newCondition(); - - public void parkNanos( final long nanos ){ - try { - waitCondition.awaitNanos(nanos); - } catch (final InterruptedException e) { - throw new RuntimeException(e); - } - } -} +package blah.concurrency.second; + +import java.util.concurrent.locks.Condition; +import java.util.concurrent.locks.ReentrantLock; + +public class Locker { + + final ReentrantLock lock = new ReentrantLock(); + final Condition waitCondition = lock.newCondition(); + + public void parkNanos( final long nanos ){ + try { + waitCondition.awaitNanos(nanos); + } catch (final InterruptedException e) { + throw new RuntimeException(e); + } + } +} diff --git a/sample/source/blah/concurrency/third/Server.java b/sample/source/blah/concurrency/third/Server.java index c4794a2..0719cda 100644 --- a/sample/source/blah/concurrency/third/Server.java +++ b/sample/source/blah/concurrency/third/Server.java @@ -1,6 +1,6 @@ -package blah.concurrency.third; - -public class Server { - - // final String -} +package blah.concurrency.third; + +public class Server { + + // final String +} diff --git a/sample/tests/blah/concurrency/FirstConsumerBenchmarkTest.java b/sample/tests/blah/concurrency/FirstConsumerBenchmarkTest.java index cef683c..bdc304b 100644 --- a/sample/tests/blah/concurrency/FirstConsumerBenchmarkTest.java +++ b/sample/tests/blah/concurrency/FirstConsumerBenchmarkTest.java @@ -1,48 +1,48 @@ -package blah.concurrency; - -import static blah.concurrency.TestDefinitions.NUMBER_OF_MESSAGES; -import static org.hamcrest.CoreMatchers.is; -import static org.junit.Assert.assertThat; - -import java.util.concurrent.CountDownLatch; - -import lombok.SneakyThrows; -import lombok.val; - -import org.junit.After; -import org.junit.Before; -import org.junit.Ignore; -import org.junit.Test; - -import blah.concurrency.first.Producer; -import blah.concurrency.helpers.Timer; - -@Ignore -public class FirstConsumerBenchmarkTest { - - final CountDownLatch counter = new CountDownLatch( NUMBER_OF_MESSAGES ); - Producer producer; - - @Before - public void setupProducersAndConsumers() { - producer = new Producer( counter ); - int processors = Runtime.getRuntime().availableProcessors(); - producer.createConsumers( processors ); - } - - @Test( timeout = 10000 ) - @SneakyThrows - public void grantThatAllConsumersHasDoneItsJobsByGrantingThatCounterIsZero() { - val timer = Timer.start(); - for ( int i = 0; i < NUMBER_OF_MESSAGES; i++ ) - producer.produce(); - counter.await(); - System.out.println( "Elapsed Time: " + timer.elapsedTime() ); - } - - @After - public void stopProducersAndConsumers() { - assertThat( counter.getCount(), is( 0l ) ); - producer.stop(); - } -} +package blah.concurrency; + +import static blah.concurrency.TestDefinitions.NUMBER_OF_MESSAGES; +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertThat; + +import java.util.concurrent.CountDownLatch; + +import lombok.SneakyThrows; +import lombok.val; + +import org.junit.After; +import org.junit.Before; +import org.junit.Ignore; +import org.junit.Test; + +import blah.concurrency.first.Producer; +import blah.concurrency.helpers.Timer; + +@Ignore +public class FirstConsumerBenchmarkTest { + + final CountDownLatch counter = new CountDownLatch( NUMBER_OF_MESSAGES ); + Producer producer; + + @Before + public void setupProducersAndConsumers() { + producer = new Producer( counter ); + int processors = Runtime.getRuntime().availableProcessors(); + producer.createConsumers( processors ); + } + + @Test( timeout = 10000 ) + @SneakyThrows + public void grantThatAllConsumersHasDoneItsJobsByGrantingThatCounterIsZero() { + val timer = Timer.start(); + for ( int i = 0; i < NUMBER_OF_MESSAGES; i++ ) + producer.produce(); + counter.await(); + System.out.println( "Elapsed Time: " + timer.elapsedTime() ); + } + + @After + public void stopProducersAndConsumers() { + assertThat( counter.getCount(), is( 0l ) ); + producer.stop(); + } +} diff --git a/sample/tests/blah/concurrency/JobQueueTest.java b/sample/tests/blah/concurrency/JobQueueTest.java index bb33d4b..8d55fe0 100644 --- a/sample/tests/blah/concurrency/JobQueueTest.java +++ b/sample/tests/blah/concurrency/JobQueueTest.java @@ -1,56 +1,56 @@ -package blah.concurrency; - -import static org.hamcrest.CoreMatchers.is; -import static org.junit.Assert.assertThat; -import static org.junit.Assert.assertTrue; - -import java.util.concurrent.Executors; - -import lombok.val; - -import org.junit.Test; - -import blah.concurrency.helpers.EmptyJob; -import blah.concurrency.helpers.LateConsumer; -import blah.concurrency.helpers.Timer; -import blah.concurrency.second.Job; -import blah.concurrency.second.JobQueue; - -public class JobQueueTest { - - static final int NEARLY_ONE_SECOND = 990; - static final int BUFFER_SIZE = 2; - - final JobQueue buffer = new JobQueue(BUFFER_SIZE); - final Job first = EmptyJob.create(1); - final Job second = EmptyJob.create(2); - - @Test( timeout=30000 ) - public void grantThatCouldPutObjectsIntoBufferAndRetrieveItInSameOrder() throws InterruptedException{ - buffer.put(first); - buffer.put(second); - assertThat( buffer.get(), is( first ) ); - assertThat( buffer.get(), is( second ) ); - } - - @Test( timeout=3000 ) - public void grantThatItWillFillTheBufferWaitUntilMoreSlotsAreAvailable() throws InterruptedException{ - scheduleToConsumeAJobInOneSecondFromNow(); - val timer = Timer.start(); - for ( int i=0; i= NEARLY_ONE_SECOND ); - } - - void scheduleToConsumeAJobInOneSecondFromNow(){ - Runnable task = new LateConsumer( buffer, 1000 ); - Executors.newSingleThreadExecutor().submit(task); - } -} +package blah.concurrency; + +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; + +import java.util.concurrent.Executors; + +import lombok.val; + +import org.junit.Test; + +import blah.concurrency.helpers.EmptyJob; +import blah.concurrency.helpers.LateConsumer; +import blah.concurrency.helpers.Timer; +import blah.concurrency.second.Job; +import blah.concurrency.second.JobQueue; + +public class JobQueueTest { + + static final int NEARLY_ONE_SECOND = 990; + static final int BUFFER_SIZE = 2; + + final JobQueue buffer = new JobQueue(BUFFER_SIZE); + final Job first = EmptyJob.create(1); + final Job second = EmptyJob.create(2); + + @Test( timeout=30000 ) + public void grantThatCouldPutObjectsIntoBufferAndRetrieveItInSameOrder() throws InterruptedException{ + buffer.put(first); + buffer.put(second); + assertThat( buffer.get(), is( first ) ); + assertThat( buffer.get(), is( second ) ); + } + + @Test( timeout=3000 ) + public void grantThatItWillFillTheBufferWaitUntilMoreSlotsAreAvailable() throws InterruptedException{ + scheduleToConsumeAJobInOneSecondFromNow(); + val timer = Timer.start(); + for ( int i=0; i= NEARLY_ONE_SECOND ); + } + + void scheduleToConsumeAJobInOneSecondFromNow(){ + Runnable task = new LateConsumer( buffer, 1000 ); + Executors.newSingleThreadExecutor().submit(task); + } +} diff --git a/sample/tests/blah/concurrency/SecondConsumerBenchmarkTest.java b/sample/tests/blah/concurrency/SecondConsumerBenchmarkTest.java index 66bfd04..5e48225 100644 --- a/sample/tests/blah/concurrency/SecondConsumerBenchmarkTest.java +++ b/sample/tests/blah/concurrency/SecondConsumerBenchmarkTest.java @@ -1,50 +1,50 @@ -package blah.concurrency; - -import static blah.concurrency.TestDefinitions.BUFFER_SIZE; -import static blah.concurrency.TestDefinitions.NUMBER_OF_MESSAGES; -import static org.hamcrest.CoreMatchers.is; -import static org.junit.Assert.assertThat; - -import java.util.concurrent.CountDownLatch; - -import org.junit.After; -import org.junit.Ignore; -import org.junit.Test; - -import blah.concurrency.helpers.ConsumerJob; -import blah.concurrency.helpers.Timer; -import blah.concurrency.second.DefaultJobDispatcher; -import blah.concurrency.second.JobDispatcher; - -@Ignore -public class SecondConsumerBenchmarkTest { - - static final int REPEAT_TIMES = 4; - static final int TOTAL_MESSAGES = REPEAT_TIMES * NUMBER_OF_MESSAGES; - - final CountDownLatch counter = new CountDownLatch( NUMBER_OF_MESSAGES ); - final JobDispatcher dispatcher = new DefaultJobDispatcher( BUFFER_SIZE ); - - @Test( timeout = 10000 ) - public void grant() throws InterruptedException { - for ( int i = 0; i < NUMBER_OF_MESSAGES; i++ ) - dispatcher.submit( new ConsumerJob( counter ) ); - counter.await(); - } - - @Test - public void grantThatCouldRunTestManyTimes() throws InterruptedException { - Timer timer = Timer.start(); - for ( int i = 0; i < REPEAT_TIMES; i++ ) - grant(); - double elapsedTime = timer.elapsedTime() / 1000; - System.out.println( "Total Elapsed time: " + elapsedTime ); - System.out.println( "Total messages: " + TOTAL_MESSAGES ); - System.out.println( "Operations per second: " + ( TOTAL_MESSAGES / elapsedTime ) ); - } - - @After - public void stopProducersAndConsumers() { - assertThat( counter.getCount(), is( 0l ) ); - } -} +package blah.concurrency; + +import static blah.concurrency.TestDefinitions.BUFFER_SIZE; +import static blah.concurrency.TestDefinitions.NUMBER_OF_MESSAGES; +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertThat; + +import java.util.concurrent.CountDownLatch; + +import org.junit.After; +import org.junit.Ignore; +import org.junit.Test; + +import blah.concurrency.helpers.ConsumerJob; +import blah.concurrency.helpers.Timer; +import blah.concurrency.second.DefaultJobDispatcher; +import blah.concurrency.second.JobDispatcher; + +@Ignore +public class SecondConsumerBenchmarkTest { + + static final int REPEAT_TIMES = 4; + static final int TOTAL_MESSAGES = REPEAT_TIMES * NUMBER_OF_MESSAGES; + + final CountDownLatch counter = new CountDownLatch( NUMBER_OF_MESSAGES ); + final JobDispatcher dispatcher = new DefaultJobDispatcher( BUFFER_SIZE ); + + @Test( timeout = 10000 ) + public void grant() throws InterruptedException { + for ( int i = 0; i < NUMBER_OF_MESSAGES; i++ ) + dispatcher.submit( new ConsumerJob( counter ) ); + counter.await(); + } + + @Test + public void grantThatCouldRunTestManyTimes() throws InterruptedException { + Timer timer = Timer.start(); + for ( int i = 0; i < REPEAT_TIMES; i++ ) + grant(); + double elapsedTime = timer.elapsedTime() / 1000; + System.out.println( "Total Elapsed time: " + elapsedTime ); + System.out.println( "Total messages: " + TOTAL_MESSAGES ); + System.out.println( "Operations per second: " + ( TOTAL_MESSAGES / elapsedTime ) ); + } + + @After + public void stopProducersAndConsumers() { + assertThat( counter.getCount(), is( 0l ) ); + } +} diff --git a/sample/tests/blah/concurrency/TestDefinitions.java b/sample/tests/blah/concurrency/TestDefinitions.java index ca28593..e9772e1 100644 --- a/sample/tests/blah/concurrency/TestDefinitions.java +++ b/sample/tests/blah/concurrency/TestDefinitions.java @@ -1,8 +1,8 @@ -package blah.concurrency; - -public class TestDefinitions { - - static final int AMOUNT_OF_CONSUMERS = 4; - static final int NUMBER_OF_MESSAGES = 10000000; - static final int BUFFER_SIZE = 50000; -} +package blah.concurrency; + +public class TestDefinitions { + + static final int AMOUNT_OF_CONSUMERS = 4; + static final int NUMBER_OF_MESSAGES = 10000000; + static final int BUFFER_SIZE = 50000; +} diff --git a/sample/tests/blah/concurrency/ThirdConsumerBenchmarkTest.java b/sample/tests/blah/concurrency/ThirdConsumerBenchmarkTest.java index fd02d67..1bca994 100644 --- a/sample/tests/blah/concurrency/ThirdConsumerBenchmarkTest.java +++ b/sample/tests/blah/concurrency/ThirdConsumerBenchmarkTest.java @@ -1,21 +1,21 @@ -package blah.concurrency; - -import static blah.concurrency.TestDefinitions.NUMBER_OF_MESSAGES; - -import java.util.concurrent.CountDownLatch; - -import org.junit.Ignore; -import org.junit.Test; - -@Ignore -public class ThirdConsumerBenchmarkTest { - - final CountDownLatch counter = new CountDownLatch( NUMBER_OF_MESSAGES ); - - @Test( timeout = 10000 ) - public void grant() throws InterruptedException { - for ( int i = 0; i < NUMBER_OF_MESSAGES; i++ ) - counter.countDown(); - counter.await(); - } -} +package blah.concurrency; + +import static blah.concurrency.TestDefinitions.NUMBER_OF_MESSAGES; + +import java.util.concurrent.CountDownLatch; + +import org.junit.Ignore; +import org.junit.Test; + +@Ignore +public class ThirdConsumerBenchmarkTest { + + final CountDownLatch counter = new CountDownLatch( NUMBER_OF_MESSAGES ); + + @Test( timeout = 10000 ) + public void grant() throws InterruptedException { + for ( int i = 0; i < NUMBER_OF_MESSAGES; i++ ) + counter.countDown(); + counter.await(); + } +} diff --git a/sample/tests/blah/concurrency/helpers/ConsumerJob.java b/sample/tests/blah/concurrency/helpers/ConsumerJob.java index 5ee7a84..f481dbb 100644 --- a/sample/tests/blah/concurrency/helpers/ConsumerJob.java +++ b/sample/tests/blah/concurrency/helpers/ConsumerJob.java @@ -1,18 +1,18 @@ -package blah.concurrency.helpers; - -import java.util.concurrent.CountDownLatch; - -import blah.concurrency.second.Job; -import blah.concurrency.second.JobDispatcher; -import lombok.RequiredArgsConstructor; - -@RequiredArgsConstructor -public class ConsumerJob implements Job { - - final CountDownLatch counter; - - @Override - public void run(JobDispatcher executor) throws InterruptedException { - counter.countDown(); - } -} +package blah.concurrency.helpers; + +import java.util.concurrent.CountDownLatch; + +import blah.concurrency.second.Job; +import blah.concurrency.second.JobDispatcher; +import lombok.RequiredArgsConstructor; + +@RequiredArgsConstructor +public class ConsumerJob implements Job { + + final CountDownLatch counter; + + @Override + public void run(JobDispatcher executor) throws InterruptedException { + counter.countDown(); + } +} diff --git a/sample/tests/blah/concurrency/helpers/EmptyJob.java b/sample/tests/blah/concurrency/helpers/EmptyJob.java index 0ccb5f5..3bb119e 100644 --- a/sample/tests/blah/concurrency/helpers/EmptyJob.java +++ b/sample/tests/blah/concurrency/helpers/EmptyJob.java @@ -1,17 +1,17 @@ -package blah.concurrency.helpers; - -import lombok.EqualsAndHashCode; -import lombok.RequiredArgsConstructor; -import blah.concurrency.second.Job; -import blah.concurrency.second.JobDispatcher; - -@EqualsAndHashCode -@RequiredArgsConstructor( staticName="create" ) -public class EmptyJob implements Job { - - final int identifier; - - @Override - public void run( JobDispatcher dispatcher ) { - } +package blah.concurrency.helpers; + +import lombok.EqualsAndHashCode; +import lombok.RequiredArgsConstructor; +import blah.concurrency.second.Job; +import blah.concurrency.second.JobDispatcher; + +@EqualsAndHashCode +@RequiredArgsConstructor( staticName="create" ) +public class EmptyJob implements Job { + + final int identifier; + + @Override + public void run( JobDispatcher dispatcher ) { + } } \ No newline at end of file diff --git a/sample/tests/blah/concurrency/helpers/LateConsumer.java b/sample/tests/blah/concurrency/helpers/LateConsumer.java index 8ab27a3..1f63909 100644 --- a/sample/tests/blah/concurrency/helpers/LateConsumer.java +++ b/sample/tests/blah/concurrency/helpers/LateConsumer.java @@ -1,21 +1,21 @@ -package blah.concurrency.helpers; - -import lombok.RequiredArgsConstructor; -import lombok.SneakyThrows; -import blah.concurrency.second.JobQueue; - -@RequiredArgsConstructor -public class LateConsumer implements Runnable { - - final JobQueue producer; - final Integer timeout; - - @Override - @SneakyThrows - public void run() { - Thread.sleep( timeout ); - System.out.println( "Produced (lately): " + producer.get() ); - } - - -} +package blah.concurrency.helpers; + +import lombok.RequiredArgsConstructor; +import lombok.SneakyThrows; +import blah.concurrency.second.JobQueue; + +@RequiredArgsConstructor +public class LateConsumer implements Runnable { + + final JobQueue producer; + final Integer timeout; + + @Override + @SneakyThrows + public void run() { + Thread.sleep( timeout ); + System.out.println( "Produced (lately): " + producer.get() ); + } + + +} diff --git a/sample/tests/blah/concurrency/helpers/Timer.java b/sample/tests/blah/concurrency/helpers/Timer.java index 81d07c3..105152a 100644 --- a/sample/tests/blah/concurrency/helpers/Timer.java +++ b/sample/tests/blah/concurrency/helpers/Timer.java @@ -1,19 +1,19 @@ -package blah.concurrency.helpers; - - -public class Timer { - - long startTime; - - public Timer() { - startTime = System.currentTimeMillis(); - } - - public static Timer start(){ - return new Timer(); - } - - public long elapsedTime() { - return System.currentTimeMillis() - startTime; - } -} +package blah.concurrency.helpers; + + +public class Timer { + + long startTime; + + public Timer() { + startTime = System.currentTimeMillis(); + } + + public static Timer start(){ + return new Timer(); + } + + public long elapsedTime() { + return System.currentTimeMillis() - startTime; + } +} diff --git a/trip-core/source/trip/spi/GeneratedFromStatelessService.java b/trip-core/source/trip/spi/GeneratedFromStatelessService.java index 7d7fc91..f824fab 100644 --- a/trip-core/source/trip/spi/GeneratedFromStatelessService.java +++ b/trip-core/source/trip/spi/GeneratedFromStatelessService.java @@ -1,13 +1,13 @@ -package trip.spi; - -import java.lang.annotation.Documented; -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -@Documented -@Retention( RetentionPolicy.RUNTIME ) -@Target( { ElementType.TYPE } ) -public @interface GeneratedFromStatelessService { -} +package trip.spi; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Documented +@Retention( RetentionPolicy.RUNTIME ) +@Target( { ElementType.TYPE } ) +public @interface GeneratedFromStatelessService { +} diff --git a/trip-core/source/trip/spi/ProducerFactory.java b/trip-core/source/trip/spi/ProducerFactory.java index c5207f0..910395f 100644 --- a/trip-core/source/trip/spi/ProducerFactory.java +++ b/trip-core/source/trip/spi/ProducerFactory.java @@ -1,7 +1,7 @@ -package trip.spi; - -public interface ProducerFactory { - - T provide( ProviderContext context ) throws ServiceProviderException; - -} +package trip.spi; + +public interface ProducerFactory { + + T provide( ProviderContext context ) throws ServiceProviderException; + +} diff --git a/trip-core/source/trip/spi/Provided.java b/trip-core/source/trip/spi/Provided.java index a3dbead..e261e31 100644 --- a/trip-core/source/trip/spi/Provided.java +++ b/trip-core/source/trip/spi/Provided.java @@ -1,23 +1,23 @@ -package trip.spi; - -import java.lang.annotation.Documented; -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -@Documented -@Retention( RetentionPolicy.RUNTIME ) -@Target( ElementType.FIELD ) -public @interface Provided { - - /** - * The name that identifies the service. - */ - String name() default ""; - - /** - * The name that identifies the service. - */ - Class exposedAs() default Provided.class; -} +package trip.spi; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Documented +@Retention( RetentionPolicy.RUNTIME ) +@Target( ElementType.FIELD ) +public @interface Provided { + + /** + * The name that identifies the service. + */ + String name() default ""; + + /** + * The name that identifies the service. + */ + Class exposedAs() default Provided.class; +} diff --git a/trip-core/source/trip/spi/ProvidedServices.java b/trip-core/source/trip/spi/ProvidedServices.java index 4e1bf26..740e9b2 100644 --- a/trip-core/source/trip/spi/ProvidedServices.java +++ b/trip-core/source/trip/spi/ProvidedServices.java @@ -1,33 +1,33 @@ -package trip.spi; - -import java.lang.annotation.Documented; -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - * Fields annotated with {@code ProvidedServices} annotation will receive all - * implementations of interface or superclass defined at {@code exposedAs} - * parameter.
- *
- * Note that it expects your field type be {@link Iterable}. Otherwise, an - * exception will be thrown in runtime. - * - * @author Miere Teixeira - */ -@Documented -@Retention( RetentionPolicy.RUNTIME ) -@Target( ElementType.FIELD ) -public @interface ProvidedServices { - - /** - * The name that identifies the service. - */ - String name() default ""; - - /** - * The name that identifies the service. - */ - Class exposedAs(); -} +package trip.spi; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Fields annotated with {@code ProvidedServices} annotation will receive all + * implementations of interface or superclass defined at {@code exposedAs} + * parameter.
+ *
+ * Note that it expects your field type be {@link Iterable}. Otherwise, an + * exception will be thrown in runtime. + * + * @author Miere Teixeira + */ +@Documented +@Retention( RetentionPolicy.RUNTIME ) +@Target( ElementType.FIELD ) +public @interface ProvidedServices { + + /** + * The name that identifies the service. + */ + String name() default ""; + + /** + * The name that identifies the service. + */ + Class exposedAs(); +} diff --git a/trip-core/source/trip/spi/ProviderContext.java b/trip-core/source/trip/spi/ProviderContext.java index 1943498..bc71cd3 100644 --- a/trip-core/source/trip/spi/ProviderContext.java +++ b/trip-core/source/trip/spi/ProviderContext.java @@ -1,45 +1,45 @@ -package trip.spi; - -import java.lang.annotation.Annotation; - -/** - * Object holding data about and provided object. It is useful when producing - * object though producer API, allowing to create a new object based on specific - * context. - */ -public interface ProviderContext { - - /** - * The list of annotations present on the target. - * - * @return - */ - A getAnnotation( Class anntationClass ); - - /** - * The type is expected to generate an object. - * - * @return - */ - Class targetType(); - - /** - * Retrieve an attribute ( identified by {@code key} ), from current - * context. Returns {@code null} if no object associated to the provided - * {@code key} was found. - * - * @param key - * @return - */ - Object attribute( String key ); - - /** - * Retrieve an attribute ( identified by {@code key} ), from current - * context. Returns {@code null} if no object associated to the provided - * {@code key} was found. - * - * @param key - * @return - */ - T attribute( Class key ); -} +package trip.spi; + +import java.lang.annotation.Annotation; + +/** + * Object holding data about and provided object. It is useful when producing + * object though producer API, allowing to create a new object based on specific + * context. + */ +public interface ProviderContext { + + /** + * The list of annotations present on the target. + * + * @return + */ + A getAnnotation( Class anntationClass ); + + /** + * The type is expected to generate an object. + * + * @return + */ + Class targetType(); + + /** + * Retrieve an attribute ( identified by {@code key} ), from current + * context. Returns {@code null} if no object associated to the provided + * {@code key} was found. + * + * @param key + * @return + */ + Object attribute( String key ); + + /** + * Retrieve an attribute ( identified by {@code key} ), from current + * context. Returns {@code null} if no object associated to the provided + * {@code key} was found. + * + * @param key + * @return + */ + T attribute( Class key ); +} diff --git a/trip-core/source/trip/spi/ServiceProvider.java b/trip-core/source/trip/spi/ServiceProvider.java index 5e95242..f2889e1 100644 --- a/trip-core/source/trip/spi/ServiceProvider.java +++ b/trip-core/source/trip/spi/ServiceProvider.java @@ -1,218 +1,218 @@ -package trip.spi; - -import java.util.HashMap; -import java.util.Map; -import java.util.ServiceConfigurationError; - -import lombok.val; -import lombok.experimental.ExtensionMethod; -import trip.spi.helpers.EmptyProviderContext; -import trip.spi.helpers.KeyValueProviderContext; -import trip.spi.helpers.ProducerFactoryMap; -import trip.spi.helpers.ProvidableClass; -import trip.spi.helpers.SingleObjectIterable; -import trip.spi.helpers.cache.CachedIterable; -import trip.spi.helpers.cache.ServiceLoader; -import trip.spi.helpers.filter.AnyClass; -import trip.spi.helpers.filter.AnyObject; -import trip.spi.helpers.filter.Condition; -import trip.spi.helpers.filter.Filter; -import trip.spi.helpers.filter.NamedClass; -import trip.spi.helpers.filter.NamedObject; - -@ExtensionMethod( Filter.class ) -public class ServiceProvider { - - final Map, ProvidableClass> providableClassCache = new HashMap, ProvidableClass>(); - final Map, Iterable>> implementedClasses = new HashMap, Iterable>>(); - final Map, Iterable> providers; - final ProducerFactoryMap producers; - - public ServiceProvider() { - this.providers = createDefaultProvidedData(); - runAllStartupListeners(); - this.producers = loadAllProducers(); - } - - void runAllStartupListeners() { - try { - final Iterable startupListeners = loadAll( StartupListener.class ); - for ( final StartupListener listener : startupListeners ) - listener.onStartup( this ); - } catch ( final ServiceProviderException e ) { - throw new IllegalStateException( e ); - } - } - - protected HashMap, Iterable> createDefaultProvidedData() { - val injectables = new HashMap, Iterable>(); - injectables.put( getClass(), new SingleObjectIterable( this ) ); - return injectables; - } - - protected ProducerFactoryMap loadAllProducers() { - try { - return ProducerFactoryMap.from( loadAll( ProducerFactory.class ) ); - } catch ( final ServiceProviderException e ) { - throw new IllegalStateException( e ); - } - } - - public T load( final Class interfaceClazz ) throws ServiceProviderException { - return load( interfaceClazz, new AnyObject() ); - } - - public T load( final Class interfaceClazz, final String name ) throws ServiceProviderException { - return load( interfaceClazz, new NamedObject( name ) ); - } - - public T load( final Class interfaceClazz, final Condition condition ) throws ServiceProviderException { - return load( interfaceClazz, condition, new EmptyProviderContext() ); - } - - public T load( final Class interfaceClazz, final ProviderContext context ) throws ServiceProviderException { - return load( interfaceClazz, new AnyObject(), context ); - } - - public T load( final Class interfaceClazz, final Map contextData ) throws ServiceProviderException { - return load( interfaceClazz, new AnyObject(), new KeyValueProviderContext( contextData ) ); - } - - public T load( final Class interfaceClazz, final String name, final Map contextData ) - throws ServiceProviderException { - return load( interfaceClazz, new NamedObject( name ), new KeyValueProviderContext( contextData ) ); - } - - public T load( final Class interfaceClazz, final Condition condition, final ProviderContext context ) - throws ServiceProviderException { - final T produced = produceFromFactory( interfaceClazz, condition, context ); - if ( produced != null ) - return produced; - return loadAll( interfaceClazz, condition ).first( condition ); - } - - @SuppressWarnings( "unchecked" ) - private ProducerFactory getProviderFor( final Class interfaceClazz, - final Condition condition ) { - if ( this.producers == null ) - return null; - return (ProducerFactory)this.producers.get( interfaceClazz, condition ); - } - - public Iterable loadAll( final Class interfaceClazz, final String name ) throws ServiceProviderException { - return loadAll( interfaceClazz, new NamedObject( name ) ); - } - - public Iterable loadAll( final Class interfaceClazz, final Condition condition ) throws ServiceProviderException { - return loadAll( interfaceClazz ).filter( condition ); - } - - @SuppressWarnings( "unchecked" ) - public Iterable loadAll( final Class interfaceClazz ) throws ServiceProviderException { - Iterable iterable = (Iterable)this.providers.get( interfaceClazz ); - if ( iterable == null ) - synchronized ( providers ) { - iterable = (Iterable)this.providers.get( interfaceClazz ); - if ( iterable == null ) - iterable = loadAllServicesImplementingTheInterface( interfaceClazz ); - } - return iterable; - } - - protected Iterable loadAllServicesImplementingTheInterface( final Class interfaceClazz ) - throws ServiceProviderException { - try { - final CachedIterable iterable = loadServiceProvidersFor( interfaceClazz ); - provideOn( iterable ); - providerFor( interfaceClazz, iterable ); - return iterable; - } catch ( final StackOverflowError cause ) { - throw new ServiceConfigurationError( - "Could not load implementations of " + interfaceClazz.getCanonicalName() + - ": Recursive dependency injection detected." ); - } - } - - protected CachedIterable loadServiceProvidersFor( - final Class interfaceClazz ) throws ServiceProviderException { - final Iterable> iterableInterfaces = loadClassesImplementing( interfaceClazz ); - return ServiceLoader.loadFrom( iterableInterfaces ); - } - - public Class loadClassImplementing( final Class interfaceClazz ) { - return loadClassImplementing( interfaceClazz, new AnyClass() ); - } - - public Class loadClassImplementing( final Class interfaceClazz, final String named ) { - return loadClassImplementing( interfaceClazz, new NamedClass( named ) ); - } - - public Class loadClassImplementing( final Class interfaceClazz, final Condition> condition ) { - return loadClassesImplementing( interfaceClazz ).first( condition ); - } - - public Iterable> loadClassesImplementing( final Class interfaceClazz, final Condition> condition ) { - return loadClassesImplementing( interfaceClazz ).filter( condition ); - } - - @SuppressWarnings( { "rawtypes", "unchecked" } ) - public Iterable> loadClassesImplementing( final Class interfaceClazz ) { - Iterable> implementations = (Iterable)implementedClasses.get( interfaceClazz ); - if ( implementations == null ) - synchronized ( implementedClasses ) { - implementations = (Iterable)implementedClasses.get( interfaceClazz ); - if ( implementations == null ) { - implementations = ServiceLoader.loadImplementationsFor( interfaceClazz ); - implementedClasses.put( (Class)interfaceClazz, (Iterable)implementations ); - } - } - return implementations; - } - - public void providerFor( final Class interfaceClazz, final ProducerFactory provider ) { - this.producers.memorizeProviderForClazz( provider, interfaceClazz ); - } - - public void providerFor( final Class interfaceClazz, final T object ) { - providerFor( interfaceClazz, new SingleObjectIterable( object ) ); - } - - protected void providerFor( final Class interfaceClazz, final Iterable iterable ) { - this.providers.put( interfaceClazz, iterable ); - } - - public void provideOn( final Iterable iterable ) throws ServiceProviderException { - for ( final T object : iterable ) - provideOn( object ); - } - - public void provideOn( final Object object ) throws ServiceProviderException { - try { - final ProvidableClass providableClass = retrieveProvidableClass( object.getClass() ); - providableClass.provide( object, this ); - } catch ( final Exception cause ) { - throw new ServiceProviderException( cause ); - } - } - - private ProvidableClass retrieveProvidableClass( final Class targetClazz ) { - ProvidableClass providableClass = providableClassCache.get( targetClazz ); - if ( providableClass == null ) - synchronized ( providableClassCache ) { - providableClass = providableClassCache.get( targetClazz ); - if ( providableClass == null ) { - providableClass = ProvidableClass.wrap( targetClazz ); - providableClassCache.put( targetClazz, providableClass ); - } - } - return providableClass; - } - - private T produceFromFactory( final Class interfaceClazz, final Condition condition, final ProviderContext context ) - throws ServiceProviderException { - final ProducerFactory provider = getProviderFor( interfaceClazz, condition ); - if ( provider != null ) - return provider.provide( context ); - return null; - } -} +package trip.spi; + +import java.util.HashMap; +import java.util.Map; +import java.util.ServiceConfigurationError; + +import lombok.val; +import lombok.experimental.ExtensionMethod; +import trip.spi.helpers.EmptyProviderContext; +import trip.spi.helpers.KeyValueProviderContext; +import trip.spi.helpers.ProducerFactoryMap; +import trip.spi.helpers.ProvidableClass; +import trip.spi.helpers.SingleObjectIterable; +import trip.spi.helpers.cache.CachedIterable; +import trip.spi.helpers.cache.ServiceLoader; +import trip.spi.helpers.filter.AnyClass; +import trip.spi.helpers.filter.AnyObject; +import trip.spi.helpers.filter.Condition; +import trip.spi.helpers.filter.Filter; +import trip.spi.helpers.filter.NamedClass; +import trip.spi.helpers.filter.NamedObject; + +@ExtensionMethod( Filter.class ) +public class ServiceProvider { + + final Map, ProvidableClass> providableClassCache = new HashMap, ProvidableClass>(); + final Map, Iterable>> implementedClasses = new HashMap, Iterable>>(); + final Map, Iterable> providers; + final ProducerFactoryMap producers; + + public ServiceProvider() { + this.providers = createDefaultProvidedData(); + runAllStartupListeners(); + this.producers = loadAllProducers(); + } + + void runAllStartupListeners() { + try { + final Iterable startupListeners = loadAll( StartupListener.class ); + for ( final StartupListener listener : startupListeners ) + listener.onStartup( this ); + } catch ( final ServiceProviderException e ) { + throw new IllegalStateException( e ); + } + } + + protected HashMap, Iterable> createDefaultProvidedData() { + val injectables = new HashMap, Iterable>(); + injectables.put( getClass(), new SingleObjectIterable( this ) ); + return injectables; + } + + protected ProducerFactoryMap loadAllProducers() { + try { + return ProducerFactoryMap.from( loadAll( ProducerFactory.class ) ); + } catch ( final ServiceProviderException e ) { + throw new IllegalStateException( e ); + } + } + + public T load( final Class interfaceClazz ) throws ServiceProviderException { + return load( interfaceClazz, new AnyObject() ); + } + + public T load( final Class interfaceClazz, final String name ) throws ServiceProviderException { + return load( interfaceClazz, new NamedObject( name ) ); + } + + public T load( final Class interfaceClazz, final Condition condition ) throws ServiceProviderException { + return load( interfaceClazz, condition, new EmptyProviderContext() ); + } + + public T load( final Class interfaceClazz, final ProviderContext context ) throws ServiceProviderException { + return load( interfaceClazz, new AnyObject(), context ); + } + + public T load( final Class interfaceClazz, final Map contextData ) throws ServiceProviderException { + return load( interfaceClazz, new AnyObject(), new KeyValueProviderContext( contextData ) ); + } + + public T load( final Class interfaceClazz, final String name, final Map contextData ) + throws ServiceProviderException { + return load( interfaceClazz, new NamedObject( name ), new KeyValueProviderContext( contextData ) ); + } + + public T load( final Class interfaceClazz, final Condition condition, final ProviderContext context ) + throws ServiceProviderException { + final T produced = produceFromFactory( interfaceClazz, condition, context ); + if ( produced != null ) + return produced; + return loadAll( interfaceClazz, condition ).first( condition ); + } + + @SuppressWarnings( "unchecked" ) + private ProducerFactory getProviderFor( final Class interfaceClazz, + final Condition condition ) { + if ( this.producers == null ) + return null; + return (ProducerFactory)this.producers.get( interfaceClazz, condition ); + } + + public Iterable loadAll( final Class interfaceClazz, final String name ) throws ServiceProviderException { + return loadAll( interfaceClazz, new NamedObject( name ) ); + } + + public Iterable loadAll( final Class interfaceClazz, final Condition condition ) throws ServiceProviderException { + return loadAll( interfaceClazz ).filter( condition ); + } + + @SuppressWarnings( "unchecked" ) + public Iterable loadAll( final Class interfaceClazz ) throws ServiceProviderException { + Iterable iterable = (Iterable)this.providers.get( interfaceClazz ); + if ( iterable == null ) + synchronized ( providers ) { + iterable = (Iterable)this.providers.get( interfaceClazz ); + if ( iterable == null ) + iterable = loadAllServicesImplementingTheInterface( interfaceClazz ); + } + return iterable; + } + + protected Iterable loadAllServicesImplementingTheInterface( final Class interfaceClazz ) + throws ServiceProviderException { + try { + final CachedIterable iterable = loadServiceProvidersFor( interfaceClazz ); + provideOn( iterable ); + providerFor( interfaceClazz, iterable ); + return iterable; + } catch ( final StackOverflowError cause ) { + throw new ServiceConfigurationError( + "Could not load implementations of " + interfaceClazz.getCanonicalName() + + ": Recursive dependency injection detected." ); + } + } + + protected CachedIterable loadServiceProvidersFor( + final Class interfaceClazz ) throws ServiceProviderException { + final Iterable> iterableInterfaces = loadClassesImplementing( interfaceClazz ); + return ServiceLoader.loadFrom( iterableInterfaces ); + } + + public Class loadClassImplementing( final Class interfaceClazz ) { + return loadClassImplementing( interfaceClazz, new AnyClass() ); + } + + public Class loadClassImplementing( final Class interfaceClazz, final String named ) { + return loadClassImplementing( interfaceClazz, new NamedClass( named ) ); + } + + public Class loadClassImplementing( final Class interfaceClazz, final Condition> condition ) { + return loadClassesImplementing( interfaceClazz ).first( condition ); + } + + public Iterable> loadClassesImplementing( final Class interfaceClazz, final Condition> condition ) { + return loadClassesImplementing( interfaceClazz ).filter( condition ); + } + + @SuppressWarnings( { "rawtypes", "unchecked" } ) + public Iterable> loadClassesImplementing( final Class interfaceClazz ) { + Iterable> implementations = (Iterable)implementedClasses.get( interfaceClazz ); + if ( implementations == null ) + synchronized ( implementedClasses ) { + implementations = (Iterable)implementedClasses.get( interfaceClazz ); + if ( implementations == null ) { + implementations = ServiceLoader.loadImplementationsFor( interfaceClazz ); + implementedClasses.put( (Class)interfaceClazz, (Iterable)implementations ); + } + } + return implementations; + } + + public void providerFor( final Class interfaceClazz, final ProducerFactory provider ) { + this.producers.memorizeProviderForClazz( provider, interfaceClazz ); + } + + public void providerFor( final Class interfaceClazz, final T object ) { + providerFor( interfaceClazz, new SingleObjectIterable( object ) ); + } + + protected void providerFor( final Class interfaceClazz, final Iterable iterable ) { + this.providers.put( interfaceClazz, iterable ); + } + + public void provideOn( final Iterable iterable ) throws ServiceProviderException { + for ( final T object : iterable ) + provideOn( object ); + } + + public void provideOn( final Object object ) throws ServiceProviderException { + try { + final ProvidableClass providableClass = retrieveProvidableClass( object.getClass() ); + providableClass.provide( object, this ); + } catch ( final Exception cause ) { + throw new ServiceProviderException( cause ); + } + } + + private ProvidableClass retrieveProvidableClass( final Class targetClazz ) { + ProvidableClass providableClass = providableClassCache.get( targetClazz ); + if ( providableClass == null ) + synchronized ( providableClassCache ) { + providableClass = providableClassCache.get( targetClazz ); + if ( providableClass == null ) { + providableClass = ProvidableClass.wrap( targetClazz ); + providableClassCache.put( targetClazz, providableClass ); + } + } + return providableClass; + } + + private T produceFromFactory( final Class interfaceClazz, final Condition condition, final ProviderContext context ) + throws ServiceProviderException { + final ProducerFactory provider = getProviderFor( interfaceClazz, condition ); + if ( provider != null ) + return provider.provide( context ); + return null; + } +} diff --git a/trip-core/source/trip/spi/ServiceProviderException.java b/trip-core/source/trip/spi/ServiceProviderException.java index cd8b246..21428ad 100644 --- a/trip-core/source/trip/spi/ServiceProviderException.java +++ b/trip-core/source/trip/spi/ServiceProviderException.java @@ -1,14 +1,14 @@ -package trip.spi; - -public class ServiceProviderException extends Exception { - - private static final long serialVersionUID = -4728985132376711824L; - - public ServiceProviderException( String message ) { - super( message ); - } - - public ServiceProviderException( Throwable cause ) { - super( cause ); - } -} +package trip.spi; + +public class ServiceProviderException extends Exception { + + private static final long serialVersionUID = -4728985132376711824L; + + public ServiceProviderException( String message ) { + super( message ); + } + + public ServiceProviderException( Throwable cause ) { + super( cause ); + } +} diff --git a/trip-core/source/trip/spi/Singleton.java b/trip-core/source/trip/spi/Singleton.java index d4f4e8c..2d84821 100644 --- a/trip-core/source/trip/spi/Singleton.java +++ b/trip-core/source/trip/spi/Singleton.java @@ -1,22 +1,22 @@ -package trip.spi; - -import java.lang.annotation.Documented; -import java.lang.annotation.ElementType; -import java.lang.annotation.Inherited; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -@Inherited -@Documented -@Retention( RetentionPolicy.RUNTIME ) -@Target( { ElementType.TYPE } ) -public @interface Singleton { - - Class exposedAs() default Singleton.class; - - /** - * The name that identifies the service. - */ - String name() default ""; -} +package trip.spi; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Inherited; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Inherited +@Documented +@Retention( RetentionPolicy.RUNTIME ) +@Target( { ElementType.TYPE } ) +public @interface Singleton { + + Class exposedAs() default Singleton.class; + + /** + * The name that identifies the service. + */ + String name() default ""; +} diff --git a/trip-core/source/trip/spi/Stateless.java b/trip-core/source/trip/spi/Stateless.java index 6c75c61..fcc2d2b 100644 --- a/trip-core/source/trip/spi/Stateless.java +++ b/trip-core/source/trip/spi/Stateless.java @@ -1,20 +1,20 @@ -package trip.spi; - -import java.lang.annotation.Documented; -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -@Documented -@Retention( RetentionPolicy.RUNTIME ) -@Target( { ElementType.TYPE } ) -public @interface Stateless { - - Class exposedAs() default Stateless.class; - - /** - * The name that identifies the service. - */ - String name() default ""; -} +package trip.spi; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Documented +@Retention( RetentionPolicy.RUNTIME ) +@Target( { ElementType.TYPE } ) +public @interface Stateless { + + Class exposedAs() default Stateless.class; + + /** + * The name that identifies the service. + */ + String name() default ""; +} diff --git a/trip-core/source/trip/spi/helpers/EmptyProviderContext.java b/trip-core/source/trip/spi/helpers/EmptyProviderContext.java index 1915175..0a4db07 100644 --- a/trip-core/source/trip/spi/helpers/EmptyProviderContext.java +++ b/trip-core/source/trip/spi/helpers/EmptyProviderContext.java @@ -1,28 +1,28 @@ -package trip.spi.helpers; - -import java.lang.annotation.Annotation; - -import trip.spi.ProviderContext; - -public class EmptyProviderContext implements ProviderContext { - - @Override - public A getAnnotation( Class anntationClass ) { - return null; - } - - @Override - public Class targetType() { - return null; - } - - @Override - public Object attribute( String key ) { - return null; - } - - @Override - public T attribute( Class key ) { - return null; - } -} +package trip.spi.helpers; + +import java.lang.annotation.Annotation; + +import trip.spi.ProviderContext; + +public class EmptyProviderContext implements ProviderContext { + + @Override + public A getAnnotation( Class anntationClass ) { + return null; + } + + @Override + public Class targetType() { + return null; + } + + @Override + public Object attribute( String key ) { + return null; + } + + @Override + public T attribute( Class key ) { + return null; + } +} diff --git a/trip-core/source/trip/spi/helpers/FieldProviderContext.java b/trip-core/source/trip/spi/helpers/FieldProviderContext.java index 8ee15e2..f6a48df 100644 --- a/trip-core/source/trip/spi/helpers/FieldProviderContext.java +++ b/trip-core/source/trip/spi/helpers/FieldProviderContext.java @@ -1,40 +1,40 @@ -package trip.spi.helpers; - -import java.lang.annotation.Annotation; -import java.lang.reflect.Field; - -import lombok.Getter; -import lombok.RequiredArgsConstructor; -import lombok.experimental.Accessors; -import trip.spi.ProviderContext; - -/** - * Holds data about a field some value should be injected. - */ -@Getter -@Accessors -@RequiredArgsConstructor -public class FieldProviderContext implements ProviderContext { - - final Field field; - - @Override - public A getAnnotation( Class annotationClass ) { - return field.getAnnotation( annotationClass ); - } - - @Override - public Class targetType() { - return field.getType(); - } - - @Override - public Object attribute( String key ) { - return null; - } - - @Override - public T attribute( Class key ) { - return null; - } -} +package trip.spi.helpers; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Field; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import lombok.experimental.Accessors; +import trip.spi.ProviderContext; + +/** + * Holds data about a field some value should be injected. + */ +@Getter +@Accessors +@RequiredArgsConstructor +public class FieldProviderContext implements ProviderContext { + + final Field field; + + @Override + public A getAnnotation( Class annotationClass ) { + return field.getAnnotation( annotationClass ); + } + + @Override + public Class targetType() { + return field.getType(); + } + + @Override + public Object attribute( String key ) { + return null; + } + + @Override + public T attribute( Class key ) { + return null; + } +} diff --git a/trip-core/source/trip/spi/helpers/KeyValueProviderContext.java b/trip-core/source/trip/spi/helpers/KeyValueProviderContext.java index f421148..cf6c6b8 100644 --- a/trip-core/source/trip/spi/helpers/KeyValueProviderContext.java +++ b/trip-core/source/trip/spi/helpers/KeyValueProviderContext.java @@ -1,53 +1,53 @@ -package trip.spi.helpers; - -import java.lang.annotation.Annotation; -import java.util.HashMap; -import java.util.Map; - -import lombok.*; -import lombok.experimental.Accessors; -import trip.spi.ProviderContext; - -@Getter -@Setter -@Accessors( fluent = true ) -@RequiredArgsConstructor -public class KeyValueProviderContext implements ProviderContext { - - final Map, Annotation> annotationMap = new HashMap, Annotation>(); - final Map attributes; - Class targetType; - - public KeyValueProviderContext() { - attributes = new HashMap(); - } - - @Override - @SuppressWarnings( "unchecked" ) - public A getAnnotation( Class anntationClass ) { - return (A)annotationMap.get( anntationClass ); - } - - public void setAnnotation( Class anntationClass, A annotation ) { - annotationMap.put( anntationClass, annotation ); - } - - public void attribute( String key, Object value ) { - attributes.put( key, value ); - } - - public void attribute( Class key, T value ) { - attributes.put( key.getCanonicalName(), value ); - } - - @Override - public Object attribute( String key ) { - return attributes.get( key ); - } - - @Override - @SuppressWarnings( "unchecked" ) - public T attribute( Class key ) { - return (T)attribute( key.getCanonicalName() ); - } -} +package trip.spi.helpers; + +import java.lang.annotation.Annotation; +import java.util.HashMap; +import java.util.Map; + +import lombok.*; +import lombok.experimental.Accessors; +import trip.spi.ProviderContext; + +@Getter +@Setter +@Accessors( fluent = true ) +@RequiredArgsConstructor +public class KeyValueProviderContext implements ProviderContext { + + final Map, Annotation> annotationMap = new HashMap, Annotation>(); + final Map attributes; + Class targetType; + + public KeyValueProviderContext() { + attributes = new HashMap(); + } + + @Override + @SuppressWarnings( "unchecked" ) + public A getAnnotation( Class anntationClass ) { + return (A)annotationMap.get( anntationClass ); + } + + public void setAnnotation( Class anntationClass, A annotation ) { + annotationMap.put( anntationClass, annotation ); + } + + public void attribute( String key, Object value ) { + attributes.put( key, value ); + } + + public void attribute( Class key, T value ) { + attributes.put( key.getCanonicalName(), value ); + } + + @Override + public Object attribute( String key ) { + return attributes.get( key ); + } + + @Override + @SuppressWarnings( "unchecked" ) + public T attribute( Class key ) { + return (T)attribute( key.getCanonicalName() ); + } +} diff --git a/trip-core/source/trip/spi/helpers/ProducerFactoryMap.java b/trip-core/source/trip/spi/helpers/ProducerFactoryMap.java index 48a88e2..5e79fd4 100644 --- a/trip-core/source/trip/spi/helpers/ProducerFactoryMap.java +++ b/trip-core/source/trip/spi/helpers/ProducerFactoryMap.java @@ -1,55 +1,55 @@ -package trip.spi.helpers; - -import java.lang.reflect.ParameterizedType; -import java.lang.reflect.Type; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import lombok.experimental.Delegate; -import lombok.experimental.ExtensionMethod; -import trip.spi.ProducerFactory; -import trip.spi.helpers.filter.Condition; -import trip.spi.helpers.filter.Filter; - -@ExtensionMethod( Filter.class ) -public class ProducerFactoryMap implements Map, List>> { - - @Delegate - final Map, List>> map = new HashMap, List>>(); - - @SuppressWarnings("rawtypes") - public static ProducerFactoryMap from( final Iterable iterable ) { - final ProducerFactoryMap providers = new ProducerFactoryMap(); - for ( final ProducerFactory provider : iterable ) { - final Class clazz = getGenericClassFrom( provider ); - providers.memorizeProviderForClazz(provider, clazz); - } - return providers; - } - - private static Class getGenericClassFrom( final ProducerFactory provider ) { - final Type[] types = provider.getClass().getGenericInterfaces(); - for ( final Type type : types ) - if ( ( (ParameterizedType)type ).getRawType().equals( ProducerFactory.class ) ) - return (Class)( (ParameterizedType)type ).getActualTypeArguments()[0]; - return null; - } - - public void memorizeProviderForClazz( final ProducerFactory provider, final Class clazz ) { - List> iterable = map.get( clazz ); - if ( iterable == null ) { - iterable = new ArrayList>(); - map.put( clazz, iterable ); - } - iterable.add( provider ); - } - - public ProducerFactory get( final Class clazz, final Condition condition ) { - final List> list = get( clazz ); - if ( list == null ) - return null; - return list.first(condition); - } -} +package trip.spi.helpers; + +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import lombok.experimental.Delegate; +import lombok.experimental.ExtensionMethod; +import trip.spi.ProducerFactory; +import trip.spi.helpers.filter.Condition; +import trip.spi.helpers.filter.Filter; + +@ExtensionMethod( Filter.class ) +public class ProducerFactoryMap implements Map, List>> { + + @Delegate + final Map, List>> map = new HashMap, List>>(); + + @SuppressWarnings("rawtypes") + public static ProducerFactoryMap from( final Iterable iterable ) { + final ProducerFactoryMap providers = new ProducerFactoryMap(); + for ( final ProducerFactory provider : iterable ) { + final Class clazz = getGenericClassFrom( provider ); + providers.memorizeProviderForClazz(provider, clazz); + } + return providers; + } + + private static Class getGenericClassFrom( final ProducerFactory provider ) { + final Type[] types = provider.getClass().getGenericInterfaces(); + for ( final Type type : types ) + if ( ( (ParameterizedType)type ).getRawType().equals( ProducerFactory.class ) ) + return (Class)( (ParameterizedType)type ).getActualTypeArguments()[0]; + return null; + } + + public void memorizeProviderForClazz( final ProducerFactory provider, final Class clazz ) { + List> iterable = map.get( clazz ); + if ( iterable == null ) { + iterable = new ArrayList>(); + map.put( clazz, iterable ); + } + iterable.add( provider ); + } + + public ProducerFactory get( final Class clazz, final Condition condition ) { + final List> list = get( clazz ); + if ( list == null ) + return null; + return list.first(condition); + } +} diff --git a/trip-core/source/trip/spi/helpers/ProvidableClass.java b/trip-core/source/trip/spi/helpers/ProvidableClass.java index 3a44d21..45ce0e0 100644 --- a/trip-core/source/trip/spi/helpers/ProvidableClass.java +++ b/trip-core/source/trip/spi/helpers/ProvidableClass.java @@ -1,49 +1,49 @@ -package trip.spi.helpers; - -import java.lang.reflect.Field; -import java.util.ArrayList; -import java.util.List; - -import lombok.RequiredArgsConstructor; -import trip.spi.GeneratedFromStatelessService; -import trip.spi.Provided; -import trip.spi.ProvidedServices; -import trip.spi.ServiceProvider; -import trip.spi.ServiceProviderException; - -@RequiredArgsConstructor -public class ProvidableClass { - - final Class targetClazz; - final Iterable fields; - - public void provide( Object instance, ServiceProvider provider ) - throws ServiceProviderException, IllegalArgumentException, IllegalAccessException { - for ( ProvidableField field : fields ) - field.provide( instance, provider ); - } - - public static ProvidableClass wrap( Class targetClazz ) { - return new ProvidableClass( targetClazz, readClassProvidableFields( targetClazz ) ); - } - - static Iterable readClassProvidableFields( Class targetClazz ) { - List providableFields = new ArrayList(); - Class clazz = targetClazz; - while ( !Object.class.equals( clazz ) ) { - populateWithProvidableFields( clazz, providableFields ); - if ( clazz.isAnnotationPresent(GeneratedFromStatelessService.class) ) - break; - clazz = clazz.getSuperclass(); - } - return providableFields; - } - - static void populateWithProvidableFields( Class targetClazz, List providableFields ) { - for ( Field field : targetClazz.getDeclaredFields() ) - if ( field.isAnnotationPresent( Provided.class ) ) - providableFields.add( SingleElementProvidableField.from( field ) ); - else if ( field.isAnnotationPresent( ProvidedServices.class ) ) - providableFields.add( ManyElementsProvidableField.from( field ) ); - } -} +package trip.spi.helpers; + +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.List; + +import lombok.RequiredArgsConstructor; +import trip.spi.GeneratedFromStatelessService; +import trip.spi.Provided; +import trip.spi.ProvidedServices; +import trip.spi.ServiceProvider; +import trip.spi.ServiceProviderException; + +@RequiredArgsConstructor +public class ProvidableClass { + + final Class targetClazz; + final Iterable fields; + + public void provide( Object instance, ServiceProvider provider ) + throws ServiceProviderException, IllegalArgumentException, IllegalAccessException { + for ( ProvidableField field : fields ) + field.provide( instance, provider ); + } + + public static ProvidableClass wrap( Class targetClazz ) { + return new ProvidableClass( targetClazz, readClassProvidableFields( targetClazz ) ); + } + + static Iterable readClassProvidableFields( Class targetClazz ) { + List providableFields = new ArrayList(); + Class clazz = targetClazz; + while ( !Object.class.equals( clazz ) ) { + populateWithProvidableFields( clazz, providableFields ); + if ( clazz.isAnnotationPresent(GeneratedFromStatelessService.class) ) + break; + clazz = clazz.getSuperclass(); + } + return providableFields; + } + + static void populateWithProvidableFields( Class targetClazz, List providableFields ) { + for ( Field field : targetClazz.getDeclaredFields() ) + if ( field.isAnnotationPresent( Provided.class ) ) + providableFields.add( SingleElementProvidableField.from( field ) ); + else if ( field.isAnnotationPresent( ProvidedServices.class ) ) + providableFields.add( ManyElementsProvidableField.from( field ) ); + } +} diff --git a/trip-core/source/trip/spi/helpers/SingleElementProvidableField.java b/trip-core/source/trip/spi/helpers/SingleElementProvidableField.java index 99a228f..1f4d357 100644 --- a/trip-core/source/trip/spi/helpers/SingleElementProvidableField.java +++ b/trip-core/source/trip/spi/helpers/SingleElementProvidableField.java @@ -1,60 +1,60 @@ -package trip.spi.helpers; - -import java.lang.reflect.Field; - -import lombok.Value; -import lombok.extern.java.Log; -import trip.spi.Provided; -import trip.spi.ProviderContext; -import trip.spi.ServiceProvider; -import trip.spi.ServiceProviderException; -import trip.spi.helpers.filter.ChainedCondition; -import trip.spi.helpers.filter.Condition; -import trip.spi.helpers.filter.IsAssignableFrom; -import trip.spi.helpers.filter.NamedObject; - -@Log -@Value -public class SingleElementProvidableField implements ProvidableField { - - final Field field; - final Class fieldType; - final Condition condition; - final ProviderContext providerContext; - - @Override - public void provide( final Object instance, final ServiceProvider provider ) - throws ServiceProviderException, IllegalArgumentException, IllegalAccessException { - final Object value = provider.load( fieldType, condition, providerContext ); - if ( value == null ) - log.warning( "No data found for " + fieldType.getCanonicalName() ); - set( instance, value ); - } - - public void set( final Object instance, final Object value ) throws IllegalArgumentException, IllegalAccessException { - field.set( instance, value ); - } - - @SuppressWarnings( { "unchecked", "rawtypes" } ) - public static ProvidableField from( final Field field ) { - field.setAccessible( true ); - final Provided provided = field.getAnnotation( Provided.class ); - final Class expectedClass = provided.exposedAs().equals( Provided.class ) - ? field.getType() : provided.exposedAs(); - return new SingleElementProvidableField( - field, (Class)expectedClass, - (Condition)extractInjectionFilterCondition( field ), - new FieldProviderContext( field ) ); - } - - public static Condition extractInjectionFilterCondition( final Field field ) { - final ChainedCondition conditions = new ChainedCondition(); - conditions.add( new IsAssignableFrom( field.getType() ) ); - - final Provided annotation = field.getAnnotation( Provided.class ); - if ( !annotation.name().isEmpty() ) - conditions.add( new NamedObject( annotation.name() ) ); - - return conditions; - } -} +package trip.spi.helpers; + +import java.lang.reflect.Field; + +import lombok.Value; +import lombok.extern.java.Log; +import trip.spi.Provided; +import trip.spi.ProviderContext; +import trip.spi.ServiceProvider; +import trip.spi.ServiceProviderException; +import trip.spi.helpers.filter.ChainedCondition; +import trip.spi.helpers.filter.Condition; +import trip.spi.helpers.filter.IsAssignableFrom; +import trip.spi.helpers.filter.NamedObject; + +@Log +@Value +public class SingleElementProvidableField implements ProvidableField { + + final Field field; + final Class fieldType; + final Condition condition; + final ProviderContext providerContext; + + @Override + public void provide( final Object instance, final ServiceProvider provider ) + throws ServiceProviderException, IllegalArgumentException, IllegalAccessException { + final Object value = provider.load( fieldType, condition, providerContext ); + if ( value == null ) + log.warning( "No data found for " + fieldType.getCanonicalName() ); + set( instance, value ); + } + + public void set( final Object instance, final Object value ) throws IllegalArgumentException, IllegalAccessException { + field.set( instance, value ); + } + + @SuppressWarnings( { "unchecked", "rawtypes" } ) + public static ProvidableField from( final Field field ) { + field.setAccessible( true ); + final Provided provided = field.getAnnotation( Provided.class ); + final Class expectedClass = provided.exposedAs().equals( Provided.class ) + ? field.getType() : provided.exposedAs(); + return new SingleElementProvidableField( + field, (Class)expectedClass, + (Condition)extractInjectionFilterCondition( field ), + new FieldProviderContext( field ) ); + } + + public static Condition extractInjectionFilterCondition( final Field field ) { + final ChainedCondition conditions = new ChainedCondition(); + conditions.add( new IsAssignableFrom( field.getType() ) ); + + final Provided annotation = field.getAnnotation( Provided.class ); + if ( !annotation.name().isEmpty() ) + conditions.add( new NamedObject( annotation.name() ) ); + + return conditions; + } +} diff --git a/trip-core/source/trip/spi/helpers/SingleObjectIterable.java b/trip-core/source/trip/spi/helpers/SingleObjectIterable.java index 128859f..1230f02 100644 --- a/trip-core/source/trip/spi/helpers/SingleObjectIterable.java +++ b/trip-core/source/trip/spi/helpers/SingleObjectIterable.java @@ -1,40 +1,40 @@ -package trip.spi.helpers; - -import java.util.Iterator; - -import lombok.RequiredArgsConstructor; - -@RequiredArgsConstructor -public class SingleObjectIterable implements Iterable { - - final T object; - - @Override - public Iterator iterator() { - return new SingleObjectIterator( object ); - } - - @RequiredArgsConstructor - public class SingleObjectIterator implements Iterator { - - final T object; - boolean firstCall = true; - - @Override - public boolean hasNext() { - boolean hasNext = firstCall; - if ( firstCall ) - firstCall = false; - return hasNext; - } - - @Override - public T next() { - return object; - } - - @Override - public void remove() { - } - } -} +package trip.spi.helpers; + +import java.util.Iterator; + +import lombok.RequiredArgsConstructor; + +@RequiredArgsConstructor +public class SingleObjectIterable implements Iterable { + + final T object; + + @Override + public Iterator iterator() { + return new SingleObjectIterator( object ); + } + + @RequiredArgsConstructor + public class SingleObjectIterator implements Iterator { + + final T object; + boolean firstCall = true; + + @Override + public boolean hasNext() { + boolean hasNext = firstCall; + if ( firstCall ) + firstCall = false; + return hasNext; + } + + @Override + public T next() { + return object; + } + + @Override + public void remove() { + } + } +} diff --git a/trip-core/source/trip/spi/helpers/cache/CachedIterable.java b/trip-core/source/trip/spi/helpers/cache/CachedIterable.java index 8cb0b76..f9112ba 100644 --- a/trip-core/source/trip/spi/helpers/cache/CachedIterable.java +++ b/trip-core/source/trip/spi/helpers/cache/CachedIterable.java @@ -1,32 +1,32 @@ -package trip.spi.helpers.cache; - -import java.util.ArrayList; -import java.util.Iterator; -import java.util.List; - -import lombok.RequiredArgsConstructor; - -@RequiredArgsConstructor -public class CachedIterable implements Iterable { - - final Iterator cachedProducer; - Iterable cache; - - @Override - public Iterator iterator() { - if ( cache == null ) - cache = createCache(); - return cache.iterator(); - } - - public Iterable createCache() { - final List cache = new ArrayList(); - while( cachedProducer.hasNext() ) - try { - cache.add( cachedProducer.next() ); - } catch ( final IllegalStateException cause ) { - continue; - } - return cache; - } -} +package trip.spi.helpers.cache; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +import lombok.RequiredArgsConstructor; + +@RequiredArgsConstructor +public class CachedIterable implements Iterable { + + final Iterator cachedProducer; + Iterable cache; + + @Override + public Iterator iterator() { + if ( cache == null ) + cache = createCache(); + return cache.iterator(); + } + + public Iterable createCache() { + final List cache = new ArrayList(); + while( cachedProducer.hasNext() ) + try { + cache.add( cachedProducer.next() ); + } catch ( final IllegalStateException cause ) { + continue; + } + return cache; + } +} diff --git a/trip-core/source/trip/spi/helpers/cache/LazyClassInstantor.java b/trip-core/source/trip/spi/helpers/cache/LazyClassInstantor.java index 39d8479..fa929fe 100644 --- a/trip-core/source/trip/spi/helpers/cache/LazyClassInstantor.java +++ b/trip-core/source/trip/spi/helpers/cache/LazyClassInstantor.java @@ -1,44 +1,44 @@ -package trip.spi.helpers.cache; - -import java.util.ArrayList; -import java.util.Iterator; -import java.util.List; - -import lombok.Getter; -import lombok.RequiredArgsConstructor; -import lombok.extern.java.Log; - -@Log -@Getter -@RequiredArgsConstructor -public class LazyClassInstantor implements Iterator { - - final Iterator> reader; - List cache = new ArrayList(); - - @Override - public boolean hasNext() { - return reader.hasNext(); - } - - @Override - public T next() { - try { - final Class clazz = reader.next(); - final T instance = clazz.newInstance(); - cache.add(instance); - return instance; - } catch ( final IllegalAccessException cause ) { - log.warning( cause.getMessage() ); - throw new IllegalStateException( cause ); - } catch ( final InstantiationException cause ) { - log.warning( cause.getMessage() ); - throw new IllegalStateException(cause); - } - } - - @Override - public void remove() { - reader.remove(); - } -} +package trip.spi.helpers.cache; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import lombok.extern.java.Log; + +@Log +@Getter +@RequiredArgsConstructor +public class LazyClassInstantor implements Iterator { + + final Iterator> reader; + List cache = new ArrayList(); + + @Override + public boolean hasNext() { + return reader.hasNext(); + } + + @Override + public T next() { + try { + final Class clazz = reader.next(); + final T instance = clazz.newInstance(); + cache.add(instance); + return instance; + } catch ( final IllegalAccessException cause ) { + log.warning( cause.getMessage() ); + throw new IllegalStateException( cause ); + } catch ( final InstantiationException cause ) { + log.warning( cause.getMessage() ); + throw new IllegalStateException(cause); + } + } + + @Override + public void remove() { + reader.remove(); + } +} diff --git a/trip-core/source/trip/spi/helpers/cache/LazyClassReader.java b/trip-core/source/trip/spi/helpers/cache/LazyClassReader.java index 62964d4..1b3eb12 100644 --- a/trip-core/source/trip/spi/helpers/cache/LazyClassReader.java +++ b/trip-core/source/trip/spi/helpers/cache/LazyClassReader.java @@ -1,115 +1,115 @@ -package trip.spi.helpers.cache; - -import java.io.BufferedReader; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.net.URL; -import java.util.ArrayList; -import java.util.Enumeration; -import java.util.Iterator; -import java.util.List; -import java.util.ServiceConfigurationError; - -import lombok.Cleanup; -import lombok.Getter; - -@Getter -public class LazyClassReader implements Iterator> { - - private static final String PREFIX = "META-INF/services/"; - private static final int NOT_FOUND = -1; - - final List> cache = new ArrayList>(); - final String serviceClassCanonicalName; - final ClassLoader loader; - final Enumeration resources; - Iterator currentResourceLines; - - public LazyClassReader( final Class serviceClass, final ClassLoader loader ) { - this( serviceClass.getCanonicalName(), loader ); - } - - public LazyClassReader( - final String serviceClassCanonicalName, - final ClassLoader loader ) { - this.serviceClassCanonicalName = serviceClassCanonicalName; - this.loader = loader; - this.resources = readAllServiceResources(); - } - - Enumeration readAllServiceResources() { - try { - final String fullName = PREFIX + serviceClassCanonicalName; - return loader.getResources( fullName ); - } catch ( final IOException cause ) { - throw new ServiceConfigurationError( serviceClassCanonicalName + ": " + cause.getMessage(), cause ); - } - } - - @Override - public boolean hasNext() { - try { - if ( currentResourceLines == null || !currentResourceLines.hasNext() ) - readNextResourceFile(); - return currentResourceLines != null && currentResourceLines.hasNext(); - } catch ( final FileNotFoundException cause ) { - return false; - } catch ( final IOException cause ) { - throw new IllegalStateException( cause ); - } - } - - void readNextResourceFile() throws IOException { - if ( getResources().hasMoreElements() ) { - final URL nextElement = getResources().nextElement(); - currentResourceLines = readLines( nextElement ); - } - } - - @Override - @SuppressWarnings( "unchecked" ) - public Class next() { - final String classCanonicalName = currentResourceLines.next(); - try { - final Class clazz = (Class)Class.forName( classCanonicalName, false, loader ); - cache.add( clazz ); - return clazz; - } catch ( final ClassNotFoundException cause ) { - throw new IllegalStateException( "Could not read class " + classCanonicalName, cause ); - } catch ( final NoClassDefFoundError cause ) { - throw new IllegalStateException( "Could not read class " + classCanonicalName, cause ); - } - } - - @Override - public void remove() { - } - - Iterator readLines( final URL url ) throws IOException { - @Cleanup - final - InputStream inputStream = url.openStream(); - @Cleanup - final - BufferedReader reader = new BufferedReader( new InputStreamReader( inputStream, "utf-8" ) ); - final List lines = new ArrayList(); - String line = null; - while ( ( line = readNextLine( reader ) ) != null ) - lines.add( line ); - return lines.iterator(); - } - - String readNextLine( final BufferedReader reader ) throws IOException { - final String ln = reader.readLine(); - if ( ln != null && !isValidClassName( ln ) ) - throw new IOException( "Invalid class name: " + ln ); - return ln; - } - - boolean isValidClassName( final String className ) { - return className.indexOf( ' ' ) == NOT_FOUND - && className.indexOf( '\t' ) == NOT_FOUND; - } +package trip.spi.helpers.cache; + +import java.io.BufferedReader; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.net.URL; +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.Iterator; +import java.util.List; +import java.util.ServiceConfigurationError; + +import lombok.Cleanup; +import lombok.Getter; + +@Getter +public class LazyClassReader implements Iterator> { + + private static final String PREFIX = "META-INF/services/"; + private static final int NOT_FOUND = -1; + + final List> cache = new ArrayList>(); + final String serviceClassCanonicalName; + final ClassLoader loader; + final Enumeration resources; + Iterator currentResourceLines; + + public LazyClassReader( final Class serviceClass, final ClassLoader loader ) { + this( serviceClass.getCanonicalName(), loader ); + } + + public LazyClassReader( + final String serviceClassCanonicalName, + final ClassLoader loader ) { + this.serviceClassCanonicalName = serviceClassCanonicalName; + this.loader = loader; + this.resources = readAllServiceResources(); + } + + Enumeration readAllServiceResources() { + try { + final String fullName = PREFIX + serviceClassCanonicalName; + return loader.getResources( fullName ); + } catch ( final IOException cause ) { + throw new ServiceConfigurationError( serviceClassCanonicalName + ": " + cause.getMessage(), cause ); + } + } + + @Override + public boolean hasNext() { + try { + if ( currentResourceLines == null || !currentResourceLines.hasNext() ) + readNextResourceFile(); + return currentResourceLines != null && currentResourceLines.hasNext(); + } catch ( final FileNotFoundException cause ) { + return false; + } catch ( final IOException cause ) { + throw new IllegalStateException( cause ); + } + } + + void readNextResourceFile() throws IOException { + if ( getResources().hasMoreElements() ) { + final URL nextElement = getResources().nextElement(); + currentResourceLines = readLines( nextElement ); + } + } + + @Override + @SuppressWarnings( "unchecked" ) + public Class next() { + final String classCanonicalName = currentResourceLines.next(); + try { + final Class clazz = (Class)Class.forName( classCanonicalName, false, loader ); + cache.add( clazz ); + return clazz; + } catch ( final ClassNotFoundException cause ) { + throw new IllegalStateException( "Could not read class " + classCanonicalName, cause ); + } catch ( final NoClassDefFoundError cause ) { + throw new IllegalStateException( "Could not read class " + classCanonicalName, cause ); + } + } + + @Override + public void remove() { + } + + Iterator readLines( final URL url ) throws IOException { + @Cleanup + final + InputStream inputStream = url.openStream(); + @Cleanup + final + BufferedReader reader = new BufferedReader( new InputStreamReader( inputStream, "utf-8" ) ); + final List lines = new ArrayList(); + String line = null; + while ( ( line = readNextLine( reader ) ) != null ) + lines.add( line ); + return lines.iterator(); + } + + String readNextLine( final BufferedReader reader ) throws IOException { + final String ln = reader.readLine(); + if ( ln != null && !isValidClassName( ln ) ) + throw new IOException( "Invalid class name: " + ln ); + return ln; + } + + boolean isValidClassName( final String className ) { + return className.indexOf( ' ' ) == NOT_FOUND + && className.indexOf( '\t' ) == NOT_FOUND; + } } \ No newline at end of file diff --git a/trip-core/source/trip/spi/helpers/cache/ServiceLoader.java b/trip-core/source/trip/spi/helpers/cache/ServiceLoader.java index f8b8d94..0be649b 100644 --- a/trip-core/source/trip/spi/helpers/cache/ServiceLoader.java +++ b/trip-core/source/trip/spi/helpers/cache/ServiceLoader.java @@ -1,26 +1,26 @@ -package trip.spi.helpers.cache; - -import java.util.Iterator; - -import lombok.RequiredArgsConstructor; - -@RequiredArgsConstructor -public class ServiceLoader { - - public static CachedIterable> loadImplementationsFor( Class clazz ) { - ClassLoader cl = Thread.currentThread().getContextClassLoader(); - Iterator> reader = new LazyClassReader(clazz, cl); - return new CachedIterable>(reader); - } - - public static CachedIterable> loadImplementationsFor( String interfaceCanonicalName ) { - ClassLoader cl = Thread.currentThread().getContextClassLoader(); - Iterator> reader = new LazyClassReader( interfaceCanonicalName, cl ); - return new CachedIterable>( reader ); - } - - public static CachedIterable loadFrom( Iterable> interfaces ) { - Iterator instantor = new LazyClassInstantor(interfaces.iterator()); - return new CachedIterable(instantor); - } -} +package trip.spi.helpers.cache; + +import java.util.Iterator; + +import lombok.RequiredArgsConstructor; + +@RequiredArgsConstructor +public class ServiceLoader { + + public static CachedIterable> loadImplementationsFor( Class clazz ) { + ClassLoader cl = Thread.currentThread().getContextClassLoader(); + Iterator> reader = new LazyClassReader(clazz, cl); + return new CachedIterable>(reader); + } + + public static CachedIterable> loadImplementationsFor( String interfaceCanonicalName ) { + ClassLoader cl = Thread.currentThread().getContextClassLoader(); + Iterator> reader = new LazyClassReader( interfaceCanonicalName, cl ); + return new CachedIterable>( reader ); + } + + public static CachedIterable loadFrom( Iterable> interfaces ) { + Iterator instantor = new LazyClassInstantor(interfaces.iterator()); + return new CachedIterable(instantor); + } +} diff --git a/trip-core/source/trip/spi/helpers/filter/AnyObject.java b/trip-core/source/trip/spi/helpers/filter/AnyObject.java index 908f8f7..e588815 100644 --- a/trip-core/source/trip/spi/helpers/filter/AnyObject.java +++ b/trip-core/source/trip/spi/helpers/filter/AnyObject.java @@ -1,12 +1,12 @@ -package trip.spi.helpers.filter; - -import lombok.NoArgsConstructor; - -@NoArgsConstructor -public class AnyObject implements Condition { - - @Override - public boolean check(Object object) { - return true; - } -} +package trip.spi.helpers.filter; + +import lombok.NoArgsConstructor; + +@NoArgsConstructor +public class AnyObject implements Condition { + + @Override + public boolean check(Object object) { + return true; + } +} diff --git a/trip-core/source/trip/spi/helpers/filter/Condition.java b/trip-core/source/trip/spi/helpers/filter/Condition.java index 2aa31c1..5db8ccd 100644 --- a/trip-core/source/trip/spi/helpers/filter/Condition.java +++ b/trip-core/source/trip/spi/helpers/filter/Condition.java @@ -1,6 +1,6 @@ -package trip.spi.helpers.filter; - -public interface Condition { - - boolean check( T object ); +package trip.spi.helpers.filter; + +public interface Condition { + + boolean check( T object ); } \ No newline at end of file diff --git a/trip-core/source/trip/spi/helpers/filter/Filter.java b/trip-core/source/trip/spi/helpers/filter/Filter.java index b452bd5..d5b67bd 100644 --- a/trip-core/source/trip/spi/helpers/filter/Filter.java +++ b/trip-core/source/trip/spi/helpers/filter/Filter.java @@ -1,24 +1,24 @@ -package trip.spi.helpers.filter; - -import java.util.ArrayList; - -import lombok.val; - -@SuppressWarnings({"unchecked", "rawtypes"}) -public class Filter { - - public static Iterable filter( Iterable self, Condition condition ) { - val list = new ArrayList(); - for ( T object : self ) - if ( condition.check(object) ) - list.add(object); - return list; - } - - public static T first( Iterable self, Condition condition ) { - for ( T object : self ) - if ( condition.check(object) ) - return object; - return null; - } -} +package trip.spi.helpers.filter; + +import java.util.ArrayList; + +import lombok.val; + +@SuppressWarnings({"unchecked", "rawtypes"}) +public class Filter { + + public static Iterable filter( Iterable self, Condition condition ) { + val list = new ArrayList(); + for ( T object : self ) + if ( condition.check(object) ) + list.add(object); + return list; + } + + public static T first( Iterable self, Condition condition ) { + for ( T object : self ) + if ( condition.check(object) ) + return object; + return null; + } +} diff --git a/trip-core/source/trip/spi/helpers/filter/NamedClass.java b/trip-core/source/trip/spi/helpers/filter/NamedClass.java index b488e4f..e60ea5a 100644 --- a/trip-core/source/trip/spi/helpers/filter/NamedClass.java +++ b/trip-core/source/trip/spi/helpers/filter/NamedClass.java @@ -1,14 +1,14 @@ -package trip.spi.helpers.filter; - -import lombok.RequiredArgsConstructor; - -@RequiredArgsConstructor -public class NamedClass implements Condition> { - - final String name; - - @Override - public boolean check(Class clazz) { - return NameExtractor.doesClassAnnotationsMatchesTheName( clazz, name ); - } -} +package trip.spi.helpers.filter; + +import lombok.RequiredArgsConstructor; + +@RequiredArgsConstructor +public class NamedClass implements Condition> { + + final String name; + + @Override + public boolean check(Class clazz) { + return NameExtractor.doesClassAnnotationsMatchesTheName( clazz, name ); + } +} diff --git a/trip-core/source/trip/spi/helpers/filter/NamedObject.java b/trip-core/source/trip/spi/helpers/filter/NamedObject.java index a262ac1..2565a9e 100644 --- a/trip-core/source/trip/spi/helpers/filter/NamedObject.java +++ b/trip-core/source/trip/spi/helpers/filter/NamedObject.java @@ -1,16 +1,16 @@ -package trip.spi.helpers.filter; - -import lombok.RequiredArgsConstructor; -import lombok.val; - -@RequiredArgsConstructor -public class NamedObject implements Condition { - - final String name; - - @Override - public boolean check( T object ) { - val clazz = object.getClass(); - return NameExtractor.doesClassAnnotationsMatchesTheName( clazz, name ); - } -} +package trip.spi.helpers.filter; + +import lombok.RequiredArgsConstructor; +import lombok.val; + +@RequiredArgsConstructor +public class NamedObject implements Condition { + + final String name; + + @Override + public boolean check( T object ) { + val clazz = object.getClass(); + return NameExtractor.doesClassAnnotationsMatchesTheName( clazz, name ); + } +} diff --git a/trip-core/tests/META-INF/services/trip.spi.Printable b/trip-core/tests/META-INF/services/trip.spi.Printable index 2d9c6b8..c26a9d3 100644 --- a/trip-core/tests/META-INF/services/trip.spi.Printable +++ b/trip-core/tests/META-INF/services/trip.spi.Printable @@ -1,2 +1,2 @@ -trip.spi.PrintableHello +trip.spi.PrintableHello invalid.Implementation \ No newline at end of file diff --git a/trip-core/tests/trip/spi/Printable.java b/trip-core/tests/trip/spi/Printable.java index 18c4175..92e5bdf 100644 --- a/trip-core/tests/trip/spi/Printable.java +++ b/trip-core/tests/trip/spi/Printable.java @@ -1,6 +1,6 @@ -package trip.spi; - -public interface Printable { - - String toString(); -} +package trip.spi; + +public interface Printable { + + String toString(); +} diff --git a/trip-core/tests/trip/spi/PrintableHello.java b/trip-core/tests/trip/spi/PrintableHello.java index c7d09c6..2ead9b8 100644 --- a/trip-core/tests/trip/spi/PrintableHello.java +++ b/trip-core/tests/trip/spi/PrintableHello.java @@ -1,15 +1,15 @@ -package trip.spi; - -import lombok.Getter; -import trip.spi.Provided; - -@Getter -public class PrintableHello implements Printable { - - @Provided PrintableWord word; - - @Override - public String toString() { - return "Hello " + word.getWord(); - } -} +package trip.spi; + +import lombok.Getter; +import trip.spi.Provided; + +@Getter +public class PrintableHello implements Printable { + + @Provided PrintableWord word; + + @Override + public String toString() { + return "Hello " + word.getWord(); + } +} diff --git a/trip-core/tests/trip/spi/PrintableWord.java b/trip-core/tests/trip/spi/PrintableWord.java index a740231..a7dbe73 100644 --- a/trip-core/tests/trip/spi/PrintableWord.java +++ b/trip-core/tests/trip/spi/PrintableWord.java @@ -1,6 +1,6 @@ -package trip.spi; - -public interface PrintableWord { - - String getWord(); -} +package trip.spi; + +public interface PrintableWord { + + String getWord(); +} diff --git a/trip-core/tests/trip/spi/PrintableWorld.java b/trip-core/tests/trip/spi/PrintableWorld.java index 20ee876..7ecd626 100644 --- a/trip-core/tests/trip/spi/PrintableWorld.java +++ b/trip-core/tests/trip/spi/PrintableWorld.java @@ -1,15 +1,15 @@ -package trip.spi; - -import lombok.Getter; - -@Getter -public class PrintableWorld implements PrintableWord { - - @Provided( name = "period" ) - Closure closure; - - @Override - public String getWord() { - return "World" + this.closure.getSentenceClosureChar(); - } -} +package trip.spi; + +import lombok.Getter; + +@Getter +public class PrintableWorld implements PrintableWord { + + @Provided( name = "period" ) + Closure closure; + + @Override + public String getWord() { + return "World" + this.closure.getSentenceClosureChar(); + } +} diff --git a/trip-core/tests/trip/spi/ProviderTest.java b/trip-core/tests/trip/spi/ProviderTest.java index 92ea575..e6933aa 100644 --- a/trip-core/tests/trip/spi/ProviderTest.java +++ b/trip-core/tests/trip/spi/ProviderTest.java @@ -1,49 +1,49 @@ -package trip.spi; - -import static org.hamcrest.CoreMatchers.is; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertThat; -import static org.junit.Assert.fail; - -import org.junit.Before; -import org.junit.Test; - -public class ProviderTest { - - final ServiceProvider provider = new ServiceProvider(); - - @Before - public void grantThatProvidedHasNoCachedData() { - Iterable nullIterable = this.provider.providers.get( Printable.class ); - assertNull(nullIterable); - nullIterable = this.provider.implementedClasses.get( Printable.class ); - assertNull(nullIterable); - } - - @Test - public void grantThatInjectTestableResourcesButKeepItCachedAsExpected() throws ServiceProviderException { - grantThatRetrieveAllClassesThatImplementsAnInterface(); - - Iterable> implementations = this.provider.implementedClasses.get( Printable.class ); - grantThatRetrieveAWellImplementedPrintableInstanceAsExpected(); - assertEquals( implementations , this.provider.implementedClasses.get( Printable.class )); - - Iterable printableInjectables = this.provider.providers.get( Printable.class ); - grantThatRetrieveAWellImplementedPrintableInstanceAsExpected(); - assertEquals( printableInjectables, this.provider.providers.get( Printable.class ) ); - } - - private void grantThatRetrieveAllClassesThatImplementsAnInterface() { - Iterable> implementations = this.provider.loadClassesImplementing( Printable.class ); - for ( Class clazz : implementations ) - if ( PrintableHello.class.equals(clazz) ) - return; - fail( "Expected to find a Printable implementation." ); - } - - private void grantThatRetrieveAWellImplementedPrintableInstanceAsExpected() throws ServiceProviderException { - Printable printable = this.provider.load( Printable.class ); - assertThat( printable.toString(), is( "Hello World." ) ); - } -} +package trip.spi; + +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.fail; + +import org.junit.Before; +import org.junit.Test; + +public class ProviderTest { + + final ServiceProvider provider = new ServiceProvider(); + + @Before + public void grantThatProvidedHasNoCachedData() { + Iterable nullIterable = this.provider.providers.get( Printable.class ); + assertNull(nullIterable); + nullIterable = this.provider.implementedClasses.get( Printable.class ); + assertNull(nullIterable); + } + + @Test + public void grantThatInjectTestableResourcesButKeepItCachedAsExpected() throws ServiceProviderException { + grantThatRetrieveAllClassesThatImplementsAnInterface(); + + Iterable> implementations = this.provider.implementedClasses.get( Printable.class ); + grantThatRetrieveAWellImplementedPrintableInstanceAsExpected(); + assertEquals( implementations , this.provider.implementedClasses.get( Printable.class )); + + Iterable printableInjectables = this.provider.providers.get( Printable.class ); + grantThatRetrieveAWellImplementedPrintableInstanceAsExpected(); + assertEquals( printableInjectables, this.provider.providers.get( Printable.class ) ); + } + + private void grantThatRetrieveAllClassesThatImplementsAnInterface() { + Iterable> implementations = this.provider.loadClassesImplementing( Printable.class ); + for ( Class clazz : implementations ) + if ( PrintableHello.class.equals(clazz) ) + return; + fail( "Expected to find a Printable implementation." ); + } + + private void grantThatRetrieveAWellImplementedPrintableInstanceAsExpected() throws ServiceProviderException { + Printable printable = this.provider.load( Printable.class ); + assertThat( printable.toString(), is( "Hello World." ) ); + } +} diff --git a/trip-core/tests/trip/spi/SuperclassInjectionTest.java b/trip-core/tests/trip/spi/SuperclassInjectionTest.java index 1fa1b82..d0ba321 100644 --- a/trip-core/tests/trip/spi/SuperclassInjectionTest.java +++ b/trip-core/tests/trip/spi/SuperclassInjectionTest.java @@ -1,37 +1,37 @@ -package trip.spi; - -import static org.hamcrest.CoreMatchers.is; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertThat; -import org.junit.Test; - -public class SuperclassInjectionTest { - - @Test - public void ensureThatInjectedOnSuperclass() throws ServiceProviderException { - final ServiceProvider provider = new ServiceProvider(); - final MyPrintable printable = new MyPrintable(); - provider.provideOn( printable ); - assertNotNull( printable.printableWord ); - assertThat( printable.toString(), is( "My trip.spi.PrintableWorld" ) ); - } -} - -class AbstractPrintable implements Printable { - - @Provided - PrintableWord printableWord; - - @Override - public String toString() { - return printableWord.toString(); - } -} - -class MyPrintable extends AbstractPrintable { - - @Override - public String toString() { - return "My " + super.toString().replaceFirst( "@.*", "" ); - } +package trip.spi; + +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertThat; +import org.junit.Test; + +public class SuperclassInjectionTest { + + @Test + public void ensureThatInjectedOnSuperclass() throws ServiceProviderException { + final ServiceProvider provider = new ServiceProvider(); + final MyPrintable printable = new MyPrintable(); + provider.provideOn( printable ); + assertNotNull( printable.printableWord ); + assertThat( printable.toString(), is( "My trip.spi.PrintableWorld" ) ); + } +} + +class AbstractPrintable implements Printable { + + @Provided + PrintableWord printableWord; + + @Override + public String toString() { + return printableWord.toString(); + } +} + +class MyPrintable extends AbstractPrintable { + + @Override + public String toString() { + return "My " + super.toString().replaceFirst( "@.*", "" ); + } } \ No newline at end of file diff --git a/trip-integration-tests/source/trip/spi/tests/Batman.java b/trip-integration-tests/source/trip/spi/tests/Batman.java index 1c5aed7..ef4e497 100644 --- a/trip-integration-tests/source/trip/spi/tests/Batman.java +++ b/trip-integration-tests/source/trip/spi/tests/Batman.java @@ -1,16 +1,16 @@ -package trip.spi.tests; - -import trip.spi.Provided; -import trip.spi.Singleton; - -@Singleton( exposedAs = Hero.class, name = "batman" ) -public class Batman implements Hero, World { - - @Provided( exposedAs = World.class ) - Mars mars; - - @Override - public String getWorld() { - return mars.getWorld(); - } -} +package trip.spi.tests; + +import trip.spi.Provided; +import trip.spi.Singleton; + +@Singleton( exposedAs = Hero.class, name = "batman" ) +public class Batman implements Hero, World { + + @Provided( exposedAs = World.class ) + Mars mars; + + @Override + public String getWorld() { + return mars.getWorld(); + } +} diff --git a/trip-integration-tests/source/trip/spi/tests/HelloFoo.java b/trip-integration-tests/source/trip/spi/tests/HelloFoo.java index 72f2ba3..65dd63f 100644 --- a/trip-integration-tests/source/trip/spi/tests/HelloFoo.java +++ b/trip-integration-tests/source/trip/spi/tests/HelloFoo.java @@ -1,9 +1,9 @@ -package trip.spi.tests; - -public class HelloFoo extends HelloWorld { - - @Override - public String toString() { - return "Fooo!!!"; - } -} +package trip.spi.tests; + +public class HelloFoo extends HelloWorld { + + @Override + public String toString() { + return "Fooo!!!"; + } +} diff --git a/trip-integration-tests/source/trip/spi/tests/ProducerOfShorts.java b/trip-integration-tests/source/trip/spi/tests/ProducerOfShorts.java index 22b804b..db76ce5 100644 --- a/trip-integration-tests/source/trip/spi/tests/ProducerOfShorts.java +++ b/trip-integration-tests/source/trip/spi/tests/ProducerOfShorts.java @@ -1,8 +1,8 @@ -package trip.spi.tests; - - -public interface ProducerOfShorts { - - public abstract Short produceShort(); - +package trip.spi.tests; + + +public interface ProducerOfShorts { + + public abstract Short produceShort(); + } \ No newline at end of file diff --git a/trip-integration-tests/source/trip/spi/tests/SerializableBean.java b/trip-integration-tests/source/trip/spi/tests/SerializableBean.java index 70e5966..ebb66e3 100644 --- a/trip-integration-tests/source/trip/spi/tests/SerializableBean.java +++ b/trip-integration-tests/source/trip/spi/tests/SerializableBean.java @@ -1,8 +1,8 @@ -package trip.spi.tests; - -import trip.spi.Stateless; - -@Stateless( exposedAs = Bean.class ) -public class SerializableBean implements Bean { - -} +package trip.spi.tests; + +import trip.spi.Stateless; + +@Stateless( exposedAs = Bean.class ) +public class SerializableBean implements Bean { + +} diff --git a/trip-integration-tests/source/trip/spi/tests/SingletonProvidedProducerOfIntegers.java b/trip-integration-tests/source/trip/spi/tests/SingletonProvidedProducerOfIntegers.java index 7f27687..13ca730 100644 --- a/trip-integration-tests/source/trip/spi/tests/SingletonProvidedProducerOfIntegers.java +++ b/trip-integration-tests/source/trip/spi/tests/SingletonProvidedProducerOfIntegers.java @@ -1,17 +1,17 @@ -package trip.spi.tests; - -import java.util.concurrent.atomic.AtomicInteger; - -import trip.spi.Producer; -import trip.spi.Singleton; - -@Singleton -public class SingletonProvidedProducerOfIntegers { - - final AtomicInteger counter = new AtomicInteger(); - - @Producer - public Integer produceInteger() { - return counter.incrementAndGet(); - } -} +package trip.spi.tests; + +import java.util.concurrent.atomic.AtomicInteger; + +import trip.spi.Producer; +import trip.spi.Singleton; + +@Singleton +public class SingletonProvidedProducerOfIntegers { + + final AtomicInteger counter = new AtomicInteger(); + + @Producer + public Integer produceInteger() { + return counter.incrementAndGet(); + } +} diff --git a/trip-integration-tests/source/trip/spi/tests/StatelessProvidedProducerOfShorts.java b/trip-integration-tests/source/trip/spi/tests/StatelessProvidedProducerOfShorts.java index 4ecf1c5..d04a03e 100644 --- a/trip-integration-tests/source/trip/spi/tests/StatelessProvidedProducerOfShorts.java +++ b/trip-integration-tests/source/trip/spi/tests/StatelessProvidedProducerOfShorts.java @@ -1,20 +1,20 @@ -package trip.spi.tests; - -import trip.spi.Producer; -import trip.spi.Stateless; - -@Stateless( exposedAs = ProducerOfShorts.class ) -public class StatelessProvidedProducerOfShorts implements ProducerOfShorts { - - volatile short counter; - - @Override - @Producer - public Short produceShort() { - return incrementAndReturn(); - } - - private short incrementAndReturn() { - return counter++; - } -} +package trip.spi.tests; + +import trip.spi.Producer; +import trip.spi.Stateless; + +@Stateless( exposedAs = ProducerOfShorts.class ) +public class StatelessProvidedProducerOfShorts implements ProducerOfShorts { + + volatile short counter; + + @Override + @Producer + public Short produceShort() { + return incrementAndReturn(); + } + + private short incrementAndReturn() { + return counter++; + } +} diff --git a/trip-integration-tests/tests/trip/spi/tests/InjectionOfServiceDefinedByItsExposedTypeTest.java b/trip-integration-tests/tests/trip/spi/tests/InjectionOfServiceDefinedByItsExposedTypeTest.java index 4410bad..2c0584f 100644 --- a/trip-integration-tests/tests/trip/spi/tests/InjectionOfServiceDefinedByItsExposedTypeTest.java +++ b/trip-integration-tests/tests/trip/spi/tests/InjectionOfServiceDefinedByItsExposedTypeTest.java @@ -1,32 +1,32 @@ -package trip.spi.tests; - -import static org.hamcrest.CoreMatchers.instanceOf; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertThat; - -import org.junit.Test; - -import trip.spi.Provided; -import trip.spi.ServiceProvider; -import trip.spi.ServiceProviderException; - -public class InjectionOfServiceDefinedByItsExposedTypeTest { - - final ServiceProvider provider = new ServiceProvider(); - - @Provided( exposedAs = Bean.class ) - SerializableBean bean; - - @Test - public void ensureThatAreAbleToLoadSerializableBeanExposedAsSerializable() throws ServiceProviderException { - final Bean loaded = provider.load( Bean.class ); - assertThat( loaded, instanceOf( SerializableBean.class ) ); - } - - @Test - public void ensureThatCouldProvideSerializableBeanExposedAsSerializable() throws ServiceProviderException { - provider.provideOn( this ); - assertNotNull( bean ); - assertThat( bean, instanceOf( SerializableBean.class ) ); - } -} +package trip.spi.tests; + +import static org.hamcrest.CoreMatchers.instanceOf; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertThat; + +import org.junit.Test; + +import trip.spi.Provided; +import trip.spi.ServiceProvider; +import trip.spi.ServiceProviderException; + +public class InjectionOfServiceDefinedByItsExposedTypeTest { + + final ServiceProvider provider = new ServiceProvider(); + + @Provided( exposedAs = Bean.class ) + SerializableBean bean; + + @Test + public void ensureThatAreAbleToLoadSerializableBeanExposedAsSerializable() throws ServiceProviderException { + final Bean loaded = provider.load( Bean.class ); + assertThat( loaded, instanceOf( SerializableBean.class ) ); + } + + @Test + public void ensureThatCouldProvideSerializableBeanExposedAsSerializable() throws ServiceProviderException { + provider.provideOn( this ); + assertNotNull( bean ); + assertThat( bean, instanceOf( SerializableBean.class ) ); + } +} diff --git a/trip-integration-tests/tests/trip/spi/tests/SingletonsAndStatelessProducerTest.java b/trip-integration-tests/tests/trip/spi/tests/SingletonsAndStatelessProducerTest.java index b839e60..86bd9b8 100644 --- a/trip-integration-tests/tests/trip/spi/tests/SingletonsAndStatelessProducerTest.java +++ b/trip-integration-tests/tests/trip/spi/tests/SingletonsAndStatelessProducerTest.java @@ -1,53 +1,53 @@ -package trip.spi.tests; - -import static org.hamcrest.CoreMatchers.is; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertThat; - -import org.junit.Test; - -import trip.spi.ServiceProvider; -import trip.spi.ServiceProviderException; - -public class SingletonsAndStatelessProducerTest { - - final ServiceProvider provider = new ServiceProvider(); - - @Test - public void ensureThatProduceThreeDifferentNumbers() throws ServiceProviderException { - assertThat( provider.load( Integer.class ), is( 1 ) ); - assertThat( provider.load( Integer.class ), is( 2 ) ); - assertThat( provider.load( Integer.class ), is( 3 ) ); - } - - @Test - public void ensureThatCantProduceThreeDifferentShorts() throws ServiceProviderException { - assertThat( provider.load( Short.class ), is( (short)0 ) ); - assertThat( provider.load( Short.class ), is( (short)0 ) ); - assertThat( provider.load( Short.class ), is( (short)0 ) ); - } - - @Test - public void ensureThatCanProduceUnrepeatedShortsWhenManuallyCreated(){ - ProducerOfShorts producerOfShorts = new StatelessProvidedProducerOfShorts(); - assertThat( producerOfShorts.produceShort(), is( (short)0 ) ); - assertThat( producerOfShorts.produceShort(), is( (short)1 ) ); - assertThat( producerOfShorts.produceShort(), is( (short)2 ) ); - } - - @Test - public void ensureThatCantProduceUnrepeatedShortsWhenCreatedByServiceProvider() throws ServiceProviderException{ - ProducerOfShorts producerOfShorts = provider.load( ProducerOfShorts.class ); - assertThat( producerOfShorts.produceShort(), is( (short)0 ) ); - producerOfShorts = provider.load( ProducerOfShorts.class ); - assertThat( producerOfShorts.produceShort(), is( (short)0 ) ); - producerOfShorts = provider.load( ProducerOfShorts.class ); - assertThat( producerOfShorts.produceShort(), is( (short)0 ) ); - } - - @Test - public void ensureThatCanListStatelessClassForProducerOfShort() { - Class implementation = provider.loadClassImplementing( ProducerOfShorts.class ); - assertNotNull( implementation ); - } -} +package trip.spi.tests; + +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertThat; + +import org.junit.Test; + +import trip.spi.ServiceProvider; +import trip.spi.ServiceProviderException; + +public class SingletonsAndStatelessProducerTest { + + final ServiceProvider provider = new ServiceProvider(); + + @Test + public void ensureThatProduceThreeDifferentNumbers() throws ServiceProviderException { + assertThat( provider.load( Integer.class ), is( 1 ) ); + assertThat( provider.load( Integer.class ), is( 2 ) ); + assertThat( provider.load( Integer.class ), is( 3 ) ); + } + + @Test + public void ensureThatCantProduceThreeDifferentShorts() throws ServiceProviderException { + assertThat( provider.load( Short.class ), is( (short)0 ) ); + assertThat( provider.load( Short.class ), is( (short)0 ) ); + assertThat( provider.load( Short.class ), is( (short)0 ) ); + } + + @Test + public void ensureThatCanProduceUnrepeatedShortsWhenManuallyCreated(){ + ProducerOfShorts producerOfShorts = new StatelessProvidedProducerOfShorts(); + assertThat( producerOfShorts.produceShort(), is( (short)0 ) ); + assertThat( producerOfShorts.produceShort(), is( (short)1 ) ); + assertThat( producerOfShorts.produceShort(), is( (short)2 ) ); + } + + @Test + public void ensureThatCantProduceUnrepeatedShortsWhenCreatedByServiceProvider() throws ServiceProviderException{ + ProducerOfShorts producerOfShorts = provider.load( ProducerOfShorts.class ); + assertThat( producerOfShorts.produceShort(), is( (short)0 ) ); + producerOfShorts = provider.load( ProducerOfShorts.class ); + assertThat( producerOfShorts.produceShort(), is( (short)0 ) ); + producerOfShorts = provider.load( ProducerOfShorts.class ); + assertThat( producerOfShorts.produceShort(), is( (short)0 ) ); + } + + @Test + public void ensureThatCanListStatelessClassForProducerOfShort() { + Class implementation = provider.loadClassImplementing( ProducerOfShorts.class ); + assertNotNull( implementation ); + } +} diff --git a/trip-processor/source/META-INF/stateless-class.mustache b/trip-processor/source/META-INF/stateless-class.mustache index 87c488a..5d07087 100644 --- a/trip-processor/source/META-INF/stateless-class.mustache +++ b/trip-processor/source/META-INF/stateless-class.mustache @@ -1,61 +1,61 @@ -package {{packageName}}; - -{{#serviceIdentificationName}} -@trip.spi.Singleton( name="{{.}}", exposedAs={{{typeCanonicalName}}}.class ) -{{/serviceIdentificationName}} -{{^serviceIdentificationName}} -@trip.spi.Singleton( exposedAs={{typeCanonicalName}}.class ) -{{/serviceIdentificationName}} -@trip.spi.GeneratedFromStatelessService -public class {{typeName}}Stateless{{identifaction}} -{{#exposedByClass}} - extends {{{typeCanonicalName}}} { -{{/exposedByClass}} -{{^exposedByClass}} - extends {{{implementationCanonicalName}}} implements {{{typeCanonicalName}}} { -{{/exposedByClass}} - - @trip.spi.Provided trip.spi.ServiceProvider provider; - {{#exposedMethods}} - - public {{{returnType}}} {{name}}( {{{parametersWithTypesAsString}}} ) { - try { - final {{{implementationCanonicalName}}} instance = newInstance(); - postConstruct{{typeName}}{{identifaction}}( instance ); - try { - {{#returnable}} - final {{{returnType}}} returnValue = instance.{{name}}( {{parametersAsString}} ); - return returnValue; - {{/returnable}} - {{^returnable}} - instance.{{name}}( {{parametersAsString}} ); - {{/returnable}} - } finally { - preDestroy{{typeName}}{{identifaction}}( instance ); - } - } catch ( Throwable cause ) { - throw new RuntimeException( cause ); - } - } - {{/exposedMethods}} - - {{{implementationCanonicalName}}} newInstance() throws trip.spi.ServiceProviderException { - final {{{implementationCanonicalName}}} instance = new {{{implementationCanonicalName}}}(); - provider.provideOn( instance ); - return instance; - } - - void postConstruct{{typeName}}{{identifaction}}( - final {{{implementationCanonicalName}}} instance ) throws Throwable { - {{#postConstructMethods}} - instance.{{name}}(); - {{/postConstructMethods}} - } - - void preDestroy{{typeName}}{{identifaction}}( - final {{{implementationCanonicalName}}} instance ) throws Throwable { - {{#preDestroyMethods}} - instance.{{name}}(); - {{/preDestroyMethods}} - } +package {{packageName}}; + +{{#serviceIdentificationName}} +@trip.spi.Singleton( name="{{.}}", exposedAs={{{typeCanonicalName}}}.class ) +{{/serviceIdentificationName}} +{{^serviceIdentificationName}} +@trip.spi.Singleton( exposedAs={{typeCanonicalName}}.class ) +{{/serviceIdentificationName}} +@trip.spi.GeneratedFromStatelessService +public class {{typeName}}Stateless{{identifaction}} +{{#exposedByClass}} + extends {{{typeCanonicalName}}} { +{{/exposedByClass}} +{{^exposedByClass}} + extends {{{implementationCanonicalName}}} implements {{{typeCanonicalName}}} { +{{/exposedByClass}} + + @trip.spi.Provided trip.spi.ServiceProvider provider; + {{#exposedMethods}} + + public {{{returnType}}} {{name}}( {{{parametersWithTypesAsString}}} ) { + try { + final {{{implementationCanonicalName}}} instance = newInstance(); + postConstruct{{typeName}}{{identifaction}}( instance ); + try { + {{#returnable}} + final {{{returnType}}} returnValue = instance.{{name}}( {{parametersAsString}} ); + return returnValue; + {{/returnable}} + {{^returnable}} + instance.{{name}}( {{parametersAsString}} ); + {{/returnable}} + } finally { + preDestroy{{typeName}}{{identifaction}}( instance ); + } + } catch ( Throwable cause ) { + throw new RuntimeException( cause ); + } + } + {{/exposedMethods}} + + {{{implementationCanonicalName}}} newInstance() throws trip.spi.ServiceProviderException { + final {{{implementationCanonicalName}}} instance = new {{{implementationCanonicalName}}}(); + provider.provideOn( instance ); + return instance; + } + + void postConstruct{{typeName}}{{identifaction}}( + final {{{implementationCanonicalName}}} instance ) throws Throwable { + {{#postConstructMethods}} + instance.{{name}}(); + {{/postConstructMethods}} + } + + void preDestroy{{typeName}}{{identifaction}}( + final {{{implementationCanonicalName}}} instance ) throws Throwable { + {{#preDestroyMethods}} + instance.{{name}}(); + {{/preDestroyMethods}} + } } \ No newline at end of file diff --git a/trip-processor/source/trip/spi/inject/NameTransformations.java b/trip-processor/source/trip/spi/inject/NameTransformations.java index 4105db4..322c79a 100644 --- a/trip-processor/source/trip/spi/inject/NameTransformations.java +++ b/trip-processor/source/trip/spi/inject/NameTransformations.java @@ -1,8 +1,8 @@ -package trip.spi.inject; - -public class NameTransformations { - - public static String stripGenericsFrom( String name ) { - return name.replaceAll("<[^>]*>", ""); - } -} +package trip.spi.inject; + +public class NameTransformations { + + public static String stripGenericsFrom( String name ) { + return name.replaceAll("<[^>]*>", ""); + } +} diff --git a/trip-processor/tests/stateless-class-exposing-class.txt b/trip-processor/tests/stateless-class-exposing-class.txt index 824942e..b4ac935 100644 --- a/trip-processor/tests/stateless-class-exposing-class.txt +++ b/trip-processor/tests/stateless-class-exposing-class.txt @@ -1,54 +1,54 @@ -package sample.project; - -@trip.spi.Singleton( name="my-self", exposedAs=sample.project.ServiceFromInterface.class ) -@trip.spi.GeneratedFromStatelessService -public class ServiceFromInterfaceStateless3994859186 - extends sample.project.ServiceFromInterface { - - @trip.spi.Provided trip.spi.ServiceProvider provider; - - public void voidMethod( ) { - try { - final sample.project.ServiceFromInterface instance = newInstance(); - postConstructServiceFromInterface3994859186( instance ); - try { - instance.voidMethod( ); - } finally { - preDestroyServiceFromInterface3994859186( instance ); - } - } catch ( Throwable cause ) { - throw new RuntimeException( cause ); - } - } - - public Long sum( Double arg0,Integer arg1 ) { - try { - final sample.project.ServiceFromInterface instance = newInstance(); - postConstructServiceFromInterface3994859186( instance ); - try { - final Long returnValue = instance.sum( arg0,arg1 ); - return returnValue; - } finally { - preDestroyServiceFromInterface3994859186( instance ); - } - } catch ( Throwable cause ) { - throw new RuntimeException( cause ); - } - } - - sample.project.ServiceFromInterface newInstance() throws trip.spi.ServiceProviderException { - final sample.project.ServiceFromInterface instance = new sample.project.ServiceFromInterface(); - provider.provideOn( instance ); - return instance; - } - - void postConstructServiceFromInterface3994859186( - final sample.project.ServiceFromInterface instance ) throws Throwable { - instance.sum(); - } - - void preDestroyServiceFromInterface3994859186( - final sample.project.ServiceFromInterface instance ) throws Throwable { - instance.voidMethod(); - } +package sample.project; + +@trip.spi.Singleton( name="my-self", exposedAs=sample.project.ServiceFromInterface.class ) +@trip.spi.GeneratedFromStatelessService +public class ServiceFromInterfaceStateless3994859186 + extends sample.project.ServiceFromInterface { + + @trip.spi.Provided trip.spi.ServiceProvider provider; + + public void voidMethod( ) { + try { + final sample.project.ServiceFromInterface instance = newInstance(); + postConstructServiceFromInterface3994859186( instance ); + try { + instance.voidMethod( ); + } finally { + preDestroyServiceFromInterface3994859186( instance ); + } + } catch ( Throwable cause ) { + throw new RuntimeException( cause ); + } + } + + public Long sum( Double arg0,Integer arg1 ) { + try { + final sample.project.ServiceFromInterface instance = newInstance(); + postConstructServiceFromInterface3994859186( instance ); + try { + final Long returnValue = instance.sum( arg0,arg1 ); + return returnValue; + } finally { + preDestroyServiceFromInterface3994859186( instance ); + } + } catch ( Throwable cause ) { + throw new RuntimeException( cause ); + } + } + + sample.project.ServiceFromInterface newInstance() throws trip.spi.ServiceProviderException { + final sample.project.ServiceFromInterface instance = new sample.project.ServiceFromInterface(); + provider.provideOn( instance ); + return instance; + } + + void postConstructServiceFromInterface3994859186( + final sample.project.ServiceFromInterface instance ) throws Throwable { + instance.sum(); + } + + void preDestroyServiceFromInterface3994859186( + final sample.project.ServiceFromInterface instance ) throws Throwable { + instance.voidMethod(); + } } \ No newline at end of file diff --git a/trip-processor/tests/stateless-class-exposing-interface.txt b/trip-processor/tests/stateless-class-exposing-interface.txt index f79c46a..e4bbccc 100644 --- a/trip-processor/tests/stateless-class-exposing-interface.txt +++ b/trip-processor/tests/stateless-class-exposing-interface.txt @@ -1,54 +1,54 @@ -package sample.project; - -@trip.spi.Singleton( exposedAs=important.api.Interface.class ) -@trip.spi.GeneratedFromStatelessService -public class InterfaceStateless1612123513 - extends sample.project.ServiceFromInterface implements important.api.Interface { - - @trip.spi.Provided trip.spi.ServiceProvider provider; - - public void voidMethod( ) { - try { - final sample.project.ServiceFromInterface instance = newInstance(); - postConstructInterface1612123513( instance ); - try { - instance.voidMethod( ); - } finally { - preDestroyInterface1612123513( instance ); - } - } catch ( Throwable cause ) { - throw new RuntimeException( cause ); - } - } - - public Long sum( Double arg0,Integer arg1 ) { - try { - final sample.project.ServiceFromInterface instance = newInstance(); - postConstructInterface1612123513( instance ); - try { - final Long returnValue = instance.sum( arg0,arg1 ); - return returnValue; - } finally { - preDestroyInterface1612123513( instance ); - } - } catch ( Throwable cause ) { - throw new RuntimeException( cause ); - } - } - - sample.project.ServiceFromInterface newInstance() throws trip.spi.ServiceProviderException { - final sample.project.ServiceFromInterface instance = new sample.project.ServiceFromInterface(); - provider.provideOn( instance ); - return instance; - } - - void postConstructInterface1612123513( - final sample.project.ServiceFromInterface instance ) throws Throwable { - instance.sum(); - } - - void preDestroyInterface1612123513( - final sample.project.ServiceFromInterface instance ) throws Throwable { - instance.voidMethod(); - } +package sample.project; + +@trip.spi.Singleton( exposedAs=important.api.Interface.class ) +@trip.spi.GeneratedFromStatelessService +public class InterfaceStateless1612123513 + extends sample.project.ServiceFromInterface implements important.api.Interface { + + @trip.spi.Provided trip.spi.ServiceProvider provider; + + public void voidMethod( ) { + try { + final sample.project.ServiceFromInterface instance = newInstance(); + postConstructInterface1612123513( instance ); + try { + instance.voidMethod( ); + } finally { + preDestroyInterface1612123513( instance ); + } + } catch ( Throwable cause ) { + throw new RuntimeException( cause ); + } + } + + public Long sum( Double arg0,Integer arg1 ) { + try { + final sample.project.ServiceFromInterface instance = newInstance(); + postConstructInterface1612123513( instance ); + try { + final Long returnValue = instance.sum( arg0,arg1 ); + return returnValue; + } finally { + preDestroyInterface1612123513( instance ); + } + } catch ( Throwable cause ) { + throw new RuntimeException( cause ); + } + } + + sample.project.ServiceFromInterface newInstance() throws trip.spi.ServiceProviderException { + final sample.project.ServiceFromInterface instance = new sample.project.ServiceFromInterface(); + provider.provideOn( instance ); + return instance; + } + + void postConstructInterface1612123513( + final sample.project.ServiceFromInterface instance ) throws Throwable { + instance.sum(); + } + + void preDestroyInterface1612123513( + final sample.project.ServiceFromInterface instance ) throws Throwable { + instance.voidMethod(); + } } \ No newline at end of file From c249f525069cb7eb3316fa16e07d0b9fbd1f0738 Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 15 Jul 2015 09:29:48 -0300 Subject: [PATCH 02/26] Closes #28 --- .../helpers/ManyElementsProvidableField.java | 10 +-------- .../helpers/SingleElementProvidableField.java | 15 +------------ .../spi/helpers/filter/NameExtractor.java | 21 ------------------- .../trip/spi/helpers/filter/NamedClass.java | 14 ------------- .../trip/spi/helpers/filter/NamedObject.java | 16 -------------- .../spi/InheritedStatelessServiceTest.java | 4 ++-- .../trip/spi/MyFakeStatelessService.java | 2 +- 7 files changed, 5 insertions(+), 77 deletions(-) delete mode 100644 trip-core/source/trip/spi/helpers/filter/NameExtractor.java delete mode 100644 trip-core/source/trip/spi/helpers/filter/NamedClass.java delete mode 100644 trip-core/source/trip/spi/helpers/filter/NamedObject.java diff --git a/trip-core/source/trip/spi/helpers/ManyElementsProvidableField.java b/trip-core/source/trip/spi/helpers/ManyElementsProvidableField.java index c9c3e58..7ec5173 100644 --- a/trip-core/source/trip/spi/helpers/ManyElementsProvidableField.java +++ b/trip-core/source/trip/spi/helpers/ManyElementsProvidableField.java @@ -9,7 +9,6 @@ import trip.spi.ServiceProviderException; import trip.spi.helpers.filter.AnyObject; import trip.spi.helpers.filter.Condition; -import trip.spi.helpers.filter.NamedObject; @RequiredArgsConstructor public class ManyElementsProvidableField implements ProvidableField { @@ -36,18 +35,11 @@ public static ProvidableField from( final Field field ) { val provided = field.getAnnotation( ProvidedServices.class ); return new ManyElementsProvidableField( field, (Class)provided.exposedAs(), - (Condition)extractInjectionFilterCondition( field ) ); + new AnyObject() ); } static void assertFieldTypeIsIterable( final Field field ) { if ( !Iterable.class.equals( field.getType() ) ) throw new IllegalStateException( "Field " + field.getName() + " expects to have Iterable type." ); } - - static Condition extractInjectionFilterCondition( final Field field ) { - val annotation = field.getAnnotation( ProvidedServices.class ); - if ( !annotation.name().isEmpty() ) - return new NamedObject( annotation.name() ); - return new AnyObject(); - } } diff --git a/trip-core/source/trip/spi/helpers/SingleElementProvidableField.java b/trip-core/source/trip/spi/helpers/SingleElementProvidableField.java index 1f4d357..da453cb 100644 --- a/trip-core/source/trip/spi/helpers/SingleElementProvidableField.java +++ b/trip-core/source/trip/spi/helpers/SingleElementProvidableField.java @@ -8,10 +8,8 @@ import trip.spi.ProviderContext; import trip.spi.ServiceProvider; import trip.spi.ServiceProviderException; -import trip.spi.helpers.filter.ChainedCondition; import trip.spi.helpers.filter.Condition; import trip.spi.helpers.filter.IsAssignableFrom; -import trip.spi.helpers.filter.NamedObject; @Log @Value @@ -43,18 +41,7 @@ public static ProvidableField from( final Field field ) { ? field.getType() : provided.exposedAs(); return new SingleElementProvidableField( field, (Class)expectedClass, - (Condition)extractInjectionFilterCondition( field ), + (Condition)new IsAssignableFrom( field.getType() ), new FieldProviderContext( field ) ); } - - public static Condition extractInjectionFilterCondition( final Field field ) { - final ChainedCondition conditions = new ChainedCondition(); - conditions.add( new IsAssignableFrom( field.getType() ) ); - - final Provided annotation = field.getAnnotation( Provided.class ); - if ( !annotation.name().isEmpty() ) - conditions.add( new NamedObject( annotation.name() ) ); - - return conditions; - } } diff --git a/trip-core/source/trip/spi/helpers/filter/NameExtractor.java b/trip-core/source/trip/spi/helpers/filter/NameExtractor.java deleted file mode 100644 index be741f9..0000000 --- a/trip-core/source/trip/spi/helpers/filter/NameExtractor.java +++ /dev/null @@ -1,21 +0,0 @@ -package trip.spi.helpers.filter; - -import trip.spi.Singleton; -import trip.spi.Stateless; - -public class NameExtractor { - - public static boolean doesClassAnnotationsMatchesTheName( Class clazz, String name ) { - return name.equals( retrieveDefinedNameFrom( clazz ) ); - } - - public static String retrieveDefinedNameFrom( Class clazz ) { - final Singleton singleton = clazz.getAnnotation( Singleton.class ); - if ( singleton != null ) - return singleton.name(); - final Stateless stateless = clazz.getAnnotation( Stateless.class ); - if ( stateless != null ) - return stateless.name(); - return null; - } -} diff --git a/trip-core/source/trip/spi/helpers/filter/NamedClass.java b/trip-core/source/trip/spi/helpers/filter/NamedClass.java deleted file mode 100644 index e60ea5a..0000000 --- a/trip-core/source/trip/spi/helpers/filter/NamedClass.java +++ /dev/null @@ -1,14 +0,0 @@ -package trip.spi.helpers.filter; - -import lombok.RequiredArgsConstructor; - -@RequiredArgsConstructor -public class NamedClass implements Condition> { - - final String name; - - @Override - public boolean check(Class clazz) { - return NameExtractor.doesClassAnnotationsMatchesTheName( clazz, name ); - } -} diff --git a/trip-core/source/trip/spi/helpers/filter/NamedObject.java b/trip-core/source/trip/spi/helpers/filter/NamedObject.java deleted file mode 100644 index 2565a9e..0000000 --- a/trip-core/source/trip/spi/helpers/filter/NamedObject.java +++ /dev/null @@ -1,16 +0,0 @@ -package trip.spi.helpers.filter; - -import lombok.RequiredArgsConstructor; -import lombok.val; - -@RequiredArgsConstructor -public class NamedObject implements Condition { - - final String name; - - @Override - public boolean check( T object ) { - val clazz = object.getClass(); - return NameExtractor.doesClassAnnotationsMatchesTheName( clazz, name ); - } -} diff --git a/trip-core/tests/trip/spi/InheritedStatelessServiceTest.java b/trip-core/tests/trip/spi/InheritedStatelessServiceTest.java index 7b96efa..431d6c7 100644 --- a/trip-core/tests/trip/spi/InheritedStatelessServiceTest.java +++ b/trip-core/tests/trip/spi/InheritedStatelessServiceTest.java @@ -13,7 +13,7 @@ public class InheritedStatelessServiceTest { @SneakyThrows public void ensureThatStatelessServiceDoesntHaveSuperClassDependenciesProvided() { val provider = new ServiceProvider(); - val fakeService = (MyFakeStatelessService)provider.load( Readable.class, "FakeStatelessService" ); + val fakeService = (MyFakeStatelessService)provider.load( Readable.class ); val superclassProvidedObject = fakeService.getWord(); assertNull( superclassProvidedObject ); } @@ -22,7 +22,7 @@ public void ensureThatStatelessServiceDoesntHaveSuperClassDependenciesProvided() @SneakyThrows public void ensureThatStatelessServiceCouldProvideDataInChildClass() { val provider = new ServiceProvider(); - val fakeService = (MyFakeStatelessService)provider.load( Readable.class, "FakeStatelessService" ); + val fakeService = (MyFakeStatelessService)provider.load( Readable.class ); val childclassProvidedObject = fakeService.getPrintable(); assertNotNull( childclassProvidedObject ); } diff --git a/trip-core/tests/trip/spi/MyFakeStatelessService.java b/trip-core/tests/trip/spi/MyFakeStatelessService.java index 591b332..4a78237 100644 --- a/trip-core/tests/trip/spi/MyFakeStatelessService.java +++ b/trip-core/tests/trip/spi/MyFakeStatelessService.java @@ -4,7 +4,7 @@ @Getter @GeneratedFromStatelessService -@Singleton( name="FakeStatelessService" ) +@Singleton public class MyFakeStatelessService extends Readable { @Provided From c7feb2276ee6b6581e4962c375224d79ea3da27c Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 15 Jul 2015 09:30:19 -0300 Subject: [PATCH 03/26] Related to #28 #29 --- .../source/trip/spi/ServiceProvider.java | 24 ------------------- 1 file changed, 24 deletions(-) diff --git a/trip-core/source/trip/spi/ServiceProvider.java b/trip-core/source/trip/spi/ServiceProvider.java index f2889e1..ac96a96 100644 --- a/trip-core/source/trip/spi/ServiceProvider.java +++ b/trip-core/source/trip/spi/ServiceProvider.java @@ -7,7 +7,6 @@ import lombok.val; import lombok.experimental.ExtensionMethod; import trip.spi.helpers.EmptyProviderContext; -import trip.spi.helpers.KeyValueProviderContext; import trip.spi.helpers.ProducerFactoryMap; import trip.spi.helpers.ProvidableClass; import trip.spi.helpers.SingleObjectIterable; @@ -17,8 +16,6 @@ import trip.spi.helpers.filter.AnyObject; import trip.spi.helpers.filter.Condition; import trip.spi.helpers.filter.Filter; -import trip.spi.helpers.filter.NamedClass; -import trip.spi.helpers.filter.NamedObject; @ExtensionMethod( Filter.class ) public class ServiceProvider { @@ -62,10 +59,6 @@ public T load( final Class interfaceClazz ) throws ServiceProviderExcepti return load( interfaceClazz, new AnyObject() ); } - public T load( final Class interfaceClazz, final String name ) throws ServiceProviderException { - return load( interfaceClazz, new NamedObject( name ) ); - } - public T load( final Class interfaceClazz, final Condition condition ) throws ServiceProviderException { return load( interfaceClazz, condition, new EmptyProviderContext() ); } @@ -74,15 +67,6 @@ public T load( final Class interfaceClazz, final ProviderContext context return load( interfaceClazz, new AnyObject(), context ); } - public T load( final Class interfaceClazz, final Map contextData ) throws ServiceProviderException { - return load( interfaceClazz, new AnyObject(), new KeyValueProviderContext( contextData ) ); - } - - public T load( final Class interfaceClazz, final String name, final Map contextData ) - throws ServiceProviderException { - return load( interfaceClazz, new NamedObject( name ), new KeyValueProviderContext( contextData ) ); - } - public T load( final Class interfaceClazz, final Condition condition, final ProviderContext context ) throws ServiceProviderException { final T produced = produceFromFactory( interfaceClazz, condition, context ); @@ -99,10 +83,6 @@ private ProducerFactory getProviderFor( final Class interfaceClazz, return (ProducerFactory)this.producers.get( interfaceClazz, condition ); } - public Iterable loadAll( final Class interfaceClazz, final String name ) throws ServiceProviderException { - return loadAll( interfaceClazz, new NamedObject( name ) ); - } - public Iterable loadAll( final Class interfaceClazz, final Condition condition ) throws ServiceProviderException { return loadAll( interfaceClazz ).filter( condition ); } @@ -143,10 +123,6 @@ public Class loadClassImplementing( final Class interfaceClazz ) { return loadClassImplementing( interfaceClazz, new AnyClass() ); } - public Class loadClassImplementing( final Class interfaceClazz, final String named ) { - return loadClassImplementing( interfaceClazz, new NamedClass( named ) ); - } - public Class loadClassImplementing( final Class interfaceClazz, final Condition> condition ) { return loadClassesImplementing( interfaceClazz ).first( condition ); } From 9cf2edd9155fff09694fc72fe1df1c61916c9a52 Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 15 Jul 2015 09:30:44 -0300 Subject: [PATCH 04/26] Removed trip-all artifact --- pom.xml | 1 - trip-all/pom.xml | 45 --------------------------------------------- 2 files changed, 46 deletions(-) delete mode 100644 trip-all/pom.xml diff --git a/pom.xml b/pom.xml index b3f52eb..a47336f 100644 --- a/pom.xml +++ b/pom.xml @@ -66,7 +66,6 @@ trip-processor trip-integration-tests sample - trip-all diff --git a/trip-all/pom.xml b/trip-all/pom.xml deleted file mode 100644 index 0cff3fb..0000000 --- a/trip-all/pom.xml +++ /dev/null @@ -1,45 +0,0 @@ - - 4.0.0 - - - io.skullabs.trip - 1.2.5-SNAPSHOT - trip-parent - - - trip-all - tRip: All - jar - - - - ${project.groupId} - trip-core - - - ${project.groupId} - trip-processor - - - com.github.spullara.mustache.java - compiler - - - - - - - org.apache.maven.plugins - maven-shade-plugin - 2.3 - - - package - shade - - - - - - From 24a7df62af7fcd01406e85c6e892d870fd217d68 Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 15 Jul 2015 09:34:26 -0300 Subject: [PATCH 05/26] Closes #31 --- trip-core/source/trip/spi/ServiceProviderException.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/trip-core/source/trip/spi/ServiceProviderException.java b/trip-core/source/trip/spi/ServiceProviderException.java index 21428ad..3e4abde 100644 --- a/trip-core/source/trip/spi/ServiceProviderException.java +++ b/trip-core/source/trip/spi/ServiceProviderException.java @@ -1,6 +1,6 @@ package trip.spi; -public class ServiceProviderException extends Exception { +public class ServiceProviderException extends RuntimeException { private static final long serialVersionUID = -4728985132376711824L; From cc11a173c55f36d84bb08370b1a32b0b197d7c55 Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 15 Jul 2015 09:37:59 -0300 Subject: [PATCH 06/26] Closes #32 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index a47336f..e432628 100644 --- a/pom.xml +++ b/pom.xml @@ -214,8 +214,8 @@ maven-compiler-plugin ${version.maven.plugin} - 1.6 - 1.6 + 1.8 + 1.8 false true UTF-8 From 69fd865329b16efdf08768cc478a7669f278ceb5 Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 15 Jul 2015 09:51:44 -0300 Subject: [PATCH 07/26] Related to #32 and #29 --- .../source/trip/spi/ServiceProvider.java | 67 +++++++++---------- .../source/trip/spi/StartupListener.java | 2 + .../trip/spi/helpers/ProducerFactoryMap.java | 4 +- 3 files changed, 37 insertions(+), 36 deletions(-) diff --git a/trip-core/source/trip/spi/ServiceProvider.java b/trip-core/source/trip/spi/ServiceProvider.java index ac96a96..2ca5eb7 100644 --- a/trip-core/source/trip/spi/ServiceProvider.java +++ b/trip-core/source/trip/spi/ServiceProvider.java @@ -1,11 +1,12 @@ package trip.spi; +import static trip.spi.helpers.filter.Filter.filter; +import static trip.spi.helpers.filter.Filter.first; + import java.util.HashMap; import java.util.Map; import java.util.ServiceConfigurationError; -import lombok.val; -import lombok.experimental.ExtensionMethod; import trip.spi.helpers.EmptyProviderContext; import trip.spi.helpers.ProducerFactoryMap; import trip.spi.helpers.ProvidableClass; @@ -15,9 +16,7 @@ import trip.spi.helpers.filter.AnyClass; import trip.spi.helpers.filter.AnyObject; import trip.spi.helpers.filter.Condition; -import trip.spi.helpers.filter.Filter; -@ExtensionMethod( Filter.class ) public class ServiceProvider { final Map, ProvidableClass> providableClassCache = new HashMap, ProvidableClass>(); @@ -27,43 +26,42 @@ public class ServiceProvider { public ServiceProvider() { this.providers = createDefaultProvidedData(); - runAllStartupListeners(); + runHookBeforeProducersAreReady(); this.producers = loadAllProducers(); + runAllStartupListeners(); } - void runAllStartupListeners() { - try { - final Iterable startupListeners = loadAll( StartupListener.class ); - for ( final StartupListener listener : startupListeners ) - listener.onStartup( this ); - } catch ( final ServiceProviderException e ) { - throw new IllegalStateException( e ); - } + private void runHookBeforeProducersAreReady() { + final Iterable startupListeners = loadAll( StartupListener.class ); + for ( final StartupListener listener : startupListeners ) + listener.beforeProducersReady( this ); } - protected HashMap, Iterable> createDefaultProvidedData() { - val injectables = new HashMap, Iterable>(); + private void runAllStartupListeners() { + final Iterable startupListeners = loadAll( StartupListener.class ); + for ( final StartupListener listener : startupListeners ) + listener.onStartup( this ); + } + + protected Map, Iterable> createDefaultProvidedData() { + final Map, Iterable> injectables = new HashMap, Iterable>(); injectables.put( getClass(), new SingleObjectIterable( this ) ); return injectables; } protected ProducerFactoryMap loadAllProducers() { - try { - return ProducerFactoryMap.from( loadAll( ProducerFactory.class ) ); - } catch ( final ServiceProviderException e ) { - throw new IllegalStateException( e ); - } + return ProducerFactoryMap.from( loadAll( ProducerFactory.class ) ); } - public T load( final Class interfaceClazz ) throws ServiceProviderException { + public T load( final Class interfaceClazz ) { return load( interfaceClazz, new AnyObject() ); } - public T load( final Class interfaceClazz, final Condition condition ) throws ServiceProviderException { + public T load( final Class interfaceClazz, final Condition condition ) { return load( interfaceClazz, condition, new EmptyProviderContext() ); } - public T load( final Class interfaceClazz, final ProviderContext context ) throws ServiceProviderException { + public T load( final Class interfaceClazz, final ProviderContext context ) { return load( interfaceClazz, new AnyObject(), context ); } @@ -72,7 +70,7 @@ public T load( final Class interfaceClazz, final Condition condition, final T produced = produceFromFactory( interfaceClazz, condition, context ); if ( produced != null ) return produced; - return loadAll( interfaceClazz, condition ).first( condition ); + return first( loadAll( interfaceClazz, condition ), condition ); } @SuppressWarnings( "unchecked" ) @@ -83,12 +81,12 @@ private ProducerFactory getProviderFor( final Class interfaceClazz, return (ProducerFactory)this.producers.get( interfaceClazz, condition ); } - public Iterable loadAll( final Class interfaceClazz, final Condition condition ) throws ServiceProviderException { - return loadAll( interfaceClazz ).filter( condition ); + public Iterable loadAll( final Class interfaceClazz, final Condition condition ) { + return filter( loadAll( interfaceClazz ), condition ); } @SuppressWarnings( "unchecked" ) - public Iterable loadAll( final Class interfaceClazz ) throws ServiceProviderException { + public Iterable loadAll( final Class interfaceClazz ) { Iterable iterable = (Iterable)this.providers.get( interfaceClazz ); if ( iterable == null ) synchronized ( providers ) { @@ -99,8 +97,7 @@ public Iterable loadAll( final Class interfaceClazz ) throws ServicePr return iterable; } - protected Iterable loadAllServicesImplementingTheInterface( final Class interfaceClazz ) - throws ServiceProviderException { + protected Iterable loadAllServicesImplementingTheInterface( final Class interfaceClazz ) { try { final CachedIterable iterable = loadServiceProvidersFor( interfaceClazz ); provideOn( iterable ); @@ -114,7 +111,7 @@ protected Iterable loadAllServicesImplementingTheInterface( final Class CachedIterable loadServiceProvidersFor( - final Class interfaceClazz ) throws ServiceProviderException { + final Class interfaceClazz ) { final Iterable> iterableInterfaces = loadClassesImplementing( interfaceClazz ); return ServiceLoader.loadFrom( iterableInterfaces ); } @@ -124,11 +121,11 @@ public Class loadClassImplementing( final Class interfaceClazz ) { } public Class loadClassImplementing( final Class interfaceClazz, final Condition> condition ) { - return loadClassesImplementing( interfaceClazz ).first( condition ); + return first( loadClassesImplementing( interfaceClazz ), condition ); } public Iterable> loadClassesImplementing( final Class interfaceClazz, final Condition> condition ) { - return loadClassesImplementing( interfaceClazz ).filter( condition ); + return filter( loadClassesImplementing( interfaceClazz ), condition ); } @SuppressWarnings( { "rawtypes", "unchecked" } ) @@ -157,12 +154,12 @@ protected void providerFor( final Class interfaceClazz, final Iterable this.providers.put( interfaceClazz, iterable ); } - public void provideOn( final Iterable iterable ) throws ServiceProviderException { + public void provideOn( final Iterable iterable ) { for ( final T object : iterable ) provideOn( object ); } - public void provideOn( final Object object ) throws ServiceProviderException { + public void provideOn( final Object object ) { try { final ProvidableClass providableClass = retrieveProvidableClass( object.getClass() ); providableClass.provide( object, this ); @@ -185,7 +182,7 @@ private ProvidableClass retrieveProvidableClass( final Class targetClazz ) } private T produceFromFactory( final Class interfaceClazz, final Condition condition, final ProviderContext context ) - throws ServiceProviderException { + { final ProducerFactory provider = getProviderFor( interfaceClazz, condition ); if ( provider != null ) return provider.provide( context ); diff --git a/trip-core/source/trip/spi/StartupListener.java b/trip-core/source/trip/spi/StartupListener.java index 9f4c0e8..445139d 100644 --- a/trip-core/source/trip/spi/StartupListener.java +++ b/trip-core/source/trip/spi/StartupListener.java @@ -2,5 +2,7 @@ public interface StartupListener { + default void beforeProducersReady( final ServiceProvider provider ){} + void onStartup( final ServiceProvider provider ); } diff --git a/trip-core/source/trip/spi/helpers/ProducerFactoryMap.java b/trip-core/source/trip/spi/helpers/ProducerFactoryMap.java index 5e79fd4..9a38fd5 100644 --- a/trip-core/source/trip/spi/helpers/ProducerFactoryMap.java +++ b/trip-core/source/trip/spi/helpers/ProducerFactoryMap.java @@ -1,5 +1,7 @@ package trip.spi.helpers; +import static trip.spi.helpers.filter.Filter.first; + import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.util.ArrayList; @@ -50,6 +52,6 @@ public ProducerFactory get( final Class clazz, final Condition conditio final List> list = get( clazz ); if ( list == null ) return null; - return list.first(condition); + return first(list, condition); } } From 8bd32cfa761dce63883e03a1b9f3879a0e06a499 Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 15 Jul 2015 15:17:12 -0300 Subject: [PATCH 08/26] Next version will 2.0.0 --- pom.xml | 44 +--------------------------------- sample/pom.xml | 2 +- trip-core/pom.xml | 2 +- trip-integration-tests/pom.xml | 8 +++---- trip-processor/pom.xml | 2 +- 5 files changed, 8 insertions(+), 50 deletions(-) diff --git a/pom.xml b/pom.xml index e432628..1e07658 100644 --- a/pom.xml +++ b/pom.xml @@ -2,7 +2,7 @@ 4.0.0 io.skullabs.trip - 1.2.5-SNAPSHOT + 2.0.0-SNAPSHOT trip-parent pom @@ -220,48 +220,6 @@ true UTF-8 - - - compile - compile - - compile - - - none - - - - test-compile - test-compile - - testCompile - - - none - - - - generate-sources - generate-sources - - compile - - - only - - - - generate-test-sources - generate-test-sources - - testCompile - - - only - - - org.eclipse.m2e diff --git a/sample/pom.xml b/sample/pom.xml index 4987672..6d66c8f 100644 --- a/sample/pom.xml +++ b/sample/pom.xml @@ -4,7 +4,7 @@ io.skullabs.trip - 1.2.5-SNAPSHOT + 2.0.0-SNAPSHOT trip-parent diff --git a/trip-core/pom.xml b/trip-core/pom.xml index cc416fc..7291128 100644 --- a/trip-core/pom.xml +++ b/trip-core/pom.xml @@ -4,7 +4,7 @@ io.skullabs.trip - 1.2.5-SNAPSHOT + 2.0.0-SNAPSHOT trip-parent diff --git a/trip-integration-tests/pom.xml b/trip-integration-tests/pom.xml index e295052..08924b6 100644 --- a/trip-integration-tests/pom.xml +++ b/trip-integration-tests/pom.xml @@ -4,7 +4,7 @@ io.skullabs.trip - 1.2.5-SNAPSHOT + 2.0.0-SNAPSHOT trip-parent @@ -15,11 +15,11 @@ ${project.groupId} - trip-all + trip-core - com.github.spullara.mustache.java - compiler + ${project.groupId} + trip-processor diff --git a/trip-processor/pom.xml b/trip-processor/pom.xml index e28a8f8..8cdfb83 100644 --- a/trip-processor/pom.xml +++ b/trip-processor/pom.xml @@ -4,7 +4,7 @@ io.skullabs.trip - 1.2.5-SNAPSHOT + 2.0.0-SNAPSHOT trip-parent From 671e4f1ce08f6c70b1bc01886d3969a94ffa8cab Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 15 Jul 2015 18:49:22 -0300 Subject: [PATCH 09/26] First implementations of #33 --- pom.xml | 4 +- sample/source/blah/tests/DateConverter.java | 7 +- sample/source/blah/tests/DateFormat.java | 12 ++ .../source/blah/tests/DefaultConverter.java | 4 +- sample/tests/blah/tests/TestProviders.java | 3 +- .../trip.spi.helpers.FieldQualifierExtractor | 1 + .../trip.spi.helpers.QualifierExtractor | 1 + .../trip/spi/DefaultServiceProvider.java | 203 ++++++++++++++++++ trip-core/source/trip/spi/Producer.java | 5 - trip-core/source/trip/spi/Provided.java | 5 - .../source/trip/spi/ProvidedServices.java | 5 - .../source/trip/spi/ProviderContext.java | 17 +- trip-core/source/trip/spi/Qualifier.java | 11 + .../source/trip/spi/ServiceProvider.java | 189 ++-------------- trip-core/source/trip/spi/Singleton.java | 5 - .../source/trip/spi/SingletonContext.java | 39 ++++ .../source/trip/spi/StartupListener.java | 2 +- trip-core/source/trip/spi/Stateless.java | 5 - .../DefaultFieldQualifierExtractor.java | 13 ++ .../spi/helpers/EmptyProviderContext.java | 9 + .../spi/helpers/FieldProviderContext.java | 4 +- .../spi/helpers/FieldQualifierExtractor.java | 19 ++ .../spi/helpers/KeyValueProviderContext.java | 11 +- .../helpers/ManyElementsProvidableField.java | 12 +- .../trip/spi/helpers/ProvidableClass.java | 33 ++- .../trip/spi/helpers/QualifierExtractor.java | 28 +++ .../helpers/SingleElementProvidableField.java | 19 +- .../spi/helpers/cache/LazyClassInstantor.java | 44 ---- .../spi/helpers/cache/LazyClassReader.java | 8 +- .../trip/spi/helpers/cache/ServiceLoader.java | 18 +- .../helpers/filter/QualifierCondition.java | 21 ++ trip-core/tests/trip/spi/ClosureProvider.java | 5 +- trip-core/tests/trip/spi/Foo.java | 11 + .../spi/InheritedStatelessServiceTest.java | 4 +- trip-core/tests/trip/spi/Period.java | 10 + trip-core/tests/trip/spi/PrintableFoo.java | 3 +- trip-core/tests/trip/spi/PrintableWorld.java | 3 +- trip-core/tests/trip/spi/ProviderTest.java | 2 +- .../SingleAndManyServicesInjectionTest.java | 5 +- .../trip/spi/SuperclassInjectionTest.java | 2 +- .../spi/helpers/QualifierConditionTest.java | 24 +++ .../spi/helpers/QualifierExtractorTest.java | 51 +++++ .../startup/ConfigurationStartupListener.java | 4 +- .../trip/spi/startup/StartupListenerTest.java | 3 +- .../source/trip/spi/tests/AjaxFromMars.java | 4 +- .../source/trip/spi/tests/Batman.java | 4 +- .../trip/spi/tests/HelloWorldProvider.java | 4 +- .../source/trip/spi/tests/ann/Ajax.java | 11 + .../source/trip/spi/tests/ann/DarkKnight.java | 11 + .../source/trip/spi/tests/ann/Foo.java | 11 + .../source/trip/spi/tests/ann/Names.java | 11 + .../spi/tests/concurrency/NameProducer.java | 4 +- .../spi/tests/concurrency/PrinterRunner.java | 4 +- .../tests/concurrency/StatelessService.java | 4 +- .../tests/trip/spi/tests/ConcurrencyTest.java | 4 +- .../tests/GeneratedCodeAndMetaINFTest.java | 28 +-- ...nOfServiceDefinedByItsExposedTypeTest.java | 4 +- ...ructAndPreDestroyStatelessServiceTest.java | 4 +- .../SingletonsAndStatelessProducerTest.java | 16 +- ...ssAndSingletonServicesAtSameInterface.java | 4 +- .../trip/spi/tests/singleton/Closeable.java | 6 + .../spi/tests/singleton/HelloWorldReader.java | 17 ++ .../trip/spi/tests/singleton/Reader.java | 6 + .../singleton/SingletonBehaviorTest.java | 32 +++ trip-jsr-330-integration-tests/.gitignore | 1 + trip-jsr-330-integration-tests/pom.xml | 30 +++ .../source/trip/jsr/cdi/ann/Ajax.java | 12 ++ .../source/trip/jsr/cdi/ann/DarkKnight.java | 11 + .../source/trip/jsr/cdi/ann/Foo.java | 11 + .../source/trip/jsr/cdi/ann/Names.java | 11 + trip-jsr-330/pom.xml | 44 ++++ .../javax.annotation.processing.Processor | 1 + .../trip/jsr/cdi/CDILoaderOfClasses.java | 25 +++ .../source/trip/jsr/cdi/CDIProcessor.java | 21 ++ .../trip/jsr/cdi/CDIServiceProvider.java | 30 +++ .../trip/jsr/cdi/OptionalCDIProcessor.java | 46 ++++ .../source/trip/jsr/cdi/ProviderWrapper.java | 18 ++ .../jsr/cdi/AnnotationWithReflectionTest.java | 22 ++ .../source/META-INF/provided-class.mustache | 8 +- .../source/META-INF/stateless-class.mustache | 5 - .../spi/inject/ProducerImplementation.java | 37 +--- .../spi/inject/ProvidedClassesProcessor.java | 69 ++++-- .../spi/inject/SingletonImplementation.java | 79 ++++--- .../spi/inject/stateless/StatelessClass.java | 22 +- .../tests/stateless-class-exposing-class.txt | 16 +- .../StatelessClassGeneratorTest.java | 5 +- 86 files changed, 1139 insertions(+), 458 deletions(-) create mode 100644 sample/source/blah/tests/DateFormat.java create mode 100644 trip-core/source/META-INF/services/trip.spi.helpers.FieldQualifierExtractor create mode 100644 trip-core/source/META-INF/services/trip.spi.helpers.QualifierExtractor create mode 100644 trip-core/source/trip/spi/DefaultServiceProvider.java create mode 100644 trip-core/source/trip/spi/Qualifier.java create mode 100644 trip-core/source/trip/spi/SingletonContext.java create mode 100644 trip-core/source/trip/spi/helpers/DefaultFieldQualifierExtractor.java create mode 100644 trip-core/source/trip/spi/helpers/FieldQualifierExtractor.java create mode 100644 trip-core/source/trip/spi/helpers/QualifierExtractor.java delete mode 100644 trip-core/source/trip/spi/helpers/cache/LazyClassInstantor.java create mode 100644 trip-core/source/trip/spi/helpers/filter/QualifierCondition.java create mode 100644 trip-core/tests/trip/spi/Foo.java create mode 100644 trip-core/tests/trip/spi/Period.java create mode 100644 trip-core/tests/trip/spi/helpers/QualifierConditionTest.java create mode 100644 trip-core/tests/trip/spi/helpers/QualifierExtractorTest.java create mode 100644 trip-integration-tests/source/trip/spi/tests/ann/Ajax.java create mode 100644 trip-integration-tests/source/trip/spi/tests/ann/DarkKnight.java create mode 100644 trip-integration-tests/source/trip/spi/tests/ann/Foo.java create mode 100644 trip-integration-tests/source/trip/spi/tests/ann/Names.java create mode 100644 trip-integration-tests/tests/trip/spi/tests/singleton/Closeable.java create mode 100644 trip-integration-tests/tests/trip/spi/tests/singleton/HelloWorldReader.java create mode 100644 trip-integration-tests/tests/trip/spi/tests/singleton/Reader.java create mode 100644 trip-integration-tests/tests/trip/spi/tests/singleton/SingletonBehaviorTest.java create mode 100644 trip-jsr-330-integration-tests/.gitignore create mode 100644 trip-jsr-330-integration-tests/pom.xml create mode 100644 trip-jsr-330-integration-tests/source/trip/jsr/cdi/ann/Ajax.java create mode 100644 trip-jsr-330-integration-tests/source/trip/jsr/cdi/ann/DarkKnight.java create mode 100644 trip-jsr-330-integration-tests/source/trip/jsr/cdi/ann/Foo.java create mode 100644 trip-jsr-330-integration-tests/source/trip/jsr/cdi/ann/Names.java create mode 100644 trip-jsr-330/pom.xml create mode 100644 trip-jsr-330/source/META-INF/services/javax.annotation.processing.Processor create mode 100644 trip-jsr-330/source/trip/jsr/cdi/CDILoaderOfClasses.java create mode 100644 trip-jsr-330/source/trip/jsr/cdi/CDIProcessor.java create mode 100644 trip-jsr-330/source/trip/jsr/cdi/CDIServiceProvider.java create mode 100644 trip-jsr-330/source/trip/jsr/cdi/OptionalCDIProcessor.java create mode 100644 trip-jsr-330/source/trip/jsr/cdi/ProviderWrapper.java create mode 100644 trip-jsr-330/tests/trip/jsr/cdi/AnnotationWithReflectionTest.java diff --git a/pom.xml b/pom.xml index 1e07658..5e0b85b 100644 --- a/pom.xml +++ b/pom.xml @@ -65,6 +65,8 @@ trip-core trip-processor trip-integration-tests + trip-jsr-330 + trip-jsr-330-integration-tests sample @@ -131,7 +133,7 @@ ${project.groupId} ${project.version} - trip-all + trip-jsr-330 diff --git a/sample/source/blah/tests/DateConverter.java b/sample/source/blah/tests/DateConverter.java index 0a6c1f6..467ba09 100644 --- a/sample/source/blah/tests/DateConverter.java +++ b/sample/source/blah/tests/DateConverter.java @@ -10,15 +10,16 @@ @Singleton( exposedAs = Converter.class ) public class DateConverter implements Converter { - @Provided( name = "date-format" ) + @Provided + @DateFormat String pattern; @Override public Date convert( String string ) throws ConverterException { try { - SimpleDateFormat formatter = new SimpleDateFormat( this.pattern ); + final SimpleDateFormat formatter = new SimpleDateFormat( this.pattern ); return formatter.parse( string ); - } catch ( ParseException cause ) { + } catch ( final ParseException cause ) { throw new ConverterException( cause ); } } diff --git a/sample/source/blah/tests/DateFormat.java b/sample/source/blah/tests/DateFormat.java new file mode 100644 index 0000000..4c980c8 --- /dev/null +++ b/sample/source/blah/tests/DateFormat.java @@ -0,0 +1,12 @@ +package blah.tests; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +import trip.spi.Qualifier; + +@Qualifier +@Retention(RetentionPolicy.RUNTIME) +public @interface DateFormat { + +} diff --git a/sample/source/blah/tests/DefaultConverter.java b/sample/source/blah/tests/DefaultConverter.java index 27ce2c0..b3a301e 100644 --- a/sample/source/blah/tests/DefaultConverter.java +++ b/sample/source/blah/tests/DefaultConverter.java @@ -3,7 +3,7 @@ import lombok.val; import lombok.experimental.Delegate; import lombok.experimental.ExtensionMethod; -import trip.spi.ServiceProvider; +import trip.spi.DefaultServiceProvider; import trip.spi.ServiceProviderException; @SuppressWarnings( "unchecked" ) @@ -19,6 +19,6 @@ public DefaultConverter( final Class targetClass ) throws ServiceProviderExce private Converter extractDefaultConverterFor( final Class targetClass ) throws ServiceProviderException { val matcher = new GenericTypeMatcher( targetClass ); - return new ServiceProvider().load( Converter.class, matcher ); + return new DefaultServiceProvider().load( Converter.class, matcher ); } } diff --git a/sample/tests/blah/tests/TestProviders.java b/sample/tests/blah/tests/TestProviders.java index 1836614..a9e223e 100644 --- a/sample/tests/blah/tests/TestProviders.java +++ b/sample/tests/blah/tests/TestProviders.java @@ -4,7 +4,8 @@ public class TestProviders { - @Producer( name = "date-format" ) + @Producer + @DateFormat public String createDateFormat() { return "yyyyMMddHHmmss"; } diff --git a/trip-core/source/META-INF/services/trip.spi.helpers.FieldQualifierExtractor b/trip-core/source/META-INF/services/trip.spi.helpers.FieldQualifierExtractor new file mode 100644 index 0000000..14348b7 --- /dev/null +++ b/trip-core/source/META-INF/services/trip.spi.helpers.FieldQualifierExtractor @@ -0,0 +1 @@ +trip.spi.helpers.DefaultFieldQualifierExtractor \ No newline at end of file diff --git a/trip-core/source/META-INF/services/trip.spi.helpers.QualifierExtractor b/trip-core/source/META-INF/services/trip.spi.helpers.QualifierExtractor new file mode 100644 index 0000000..f4ada1b --- /dev/null +++ b/trip-core/source/META-INF/services/trip.spi.helpers.QualifierExtractor @@ -0,0 +1 @@ +trip.spi.helpers.QualifierExtractor \ No newline at end of file diff --git a/trip-core/source/trip/spi/DefaultServiceProvider.java b/trip-core/source/trip/spi/DefaultServiceProvider.java new file mode 100644 index 0000000..539c09f --- /dev/null +++ b/trip-core/source/trip/spi/DefaultServiceProvider.java @@ -0,0 +1,203 @@ +package trip.spi; + +import static trip.spi.helpers.filter.Filter.filter; +import static trip.spi.helpers.filter.Filter.first; + +import java.util.HashMap; +import java.util.Map; +import java.util.ServiceConfigurationError; + +import trip.spi.helpers.EmptyProviderContext; +import trip.spi.helpers.FieldQualifierExtractor; +import trip.spi.helpers.ProducerFactoryMap; +import trip.spi.helpers.ProvidableClass; +import trip.spi.helpers.QualifierExtractor; +import trip.spi.helpers.SingleObjectIterable; +import trip.spi.helpers.cache.ServiceLoader; +import trip.spi.helpers.filter.AnyObject; +import trip.spi.helpers.filter.Condition; + +public class DefaultServiceProvider implements ServiceProvider { + + final SingletonContext singletonContext = new SingletonContext(); + final Map, ProvidableClass> providableClassCache = new HashMap<>(); + final Map, Iterable>> implementedClasses = new HashMap<>(); + + final QualifierExtractor qualifierExtractor; + final Map, Iterable> providers; + final ProducerFactoryMap producers; + + public DefaultServiceProvider() { + this.providers = createDefaultProvidedData(); + this.qualifierExtractor = createQualifierExtractor(); + runHookBeforeProducersAreReady(); + this.producers = loadAllProducers(); + runAllStartupListeners(); + } + + private QualifierExtractor createQualifierExtractor() { + final Iterable extractors = loadAll(FieldQualifierExtractor.class); + return new QualifierExtractor( extractors ); + } + + private void runHookBeforeProducersAreReady() { + final Iterable startupListeners = loadAll( StartupListener.class ); + for ( final StartupListener listener : startupListeners ) + listener.beforeProducersReady( this ); + } + + private void runAllStartupListeners() { + final Iterable startupListeners = loadAll( StartupListener.class ); + for ( final StartupListener listener : startupListeners ) + listener.onStartup( this ); + } + + protected Map, Iterable> createDefaultProvidedData() { + final Map, Iterable> injectables = new HashMap, Iterable>(); + injectables.put( ServiceProvider.class, new SingleObjectIterable( this ) ); + return injectables; + } + + protected ProducerFactoryMap loadAllProducers() { + return ProducerFactoryMap.from( loadAll( ProducerFactory.class ) ); + } + + @Override + public T load( final Class interfaceClazz ) { + return load( interfaceClazz, new AnyObject() ); + } + + @Override + public T load( final Class interfaceClazz, final Condition condition ) { + return load( interfaceClazz, condition, new EmptyProviderContext() ); + } + + @Override + public T load( final Class interfaceClazz, final ProviderContext context ) { + return load( interfaceClazz, new AnyObject(), context ); + } + + @Override + public T load( final Class interfaceClazz, final Condition condition, final ProviderContext context ) + throws ServiceProviderException { + final T produced = produceFromFactory( interfaceClazz, condition, context ); + if ( produced != null ) + return produced; + return first( loadAll( interfaceClazz, condition ), condition ); + } + + @Override + public Iterable loadAll( final Class interfaceClazz, final Condition condition ) { + return filter( loadAll( interfaceClazz ), condition ); + } + + @Override + @SuppressWarnings( "unchecked" ) + public Iterable loadAll( final Class interfaceClazz ) { + Iterable iterable = (Iterable)this.providers.get( interfaceClazz ); + if ( iterable == null ) + synchronized ( providers ) { + iterable = (Iterable)this.providers.get( interfaceClazz ); + if ( iterable == null ) + iterable = loadAllServicesImplementingTheInterface( interfaceClazz ); + } + return iterable; + } + + protected Iterable loadAllServicesImplementingTheInterface( final Class interfaceClazz ) { + try { + final Iterable iterable = loadServiceProvidersFor( interfaceClazz ); + provideOn( iterable ); + providerFor( interfaceClazz, iterable ); + return iterable; + } catch ( final StackOverflowError cause ) { + throw new ServiceConfigurationError( + "Could not load implementations of " + interfaceClazz.getCanonicalName() + + ": Recursive dependency injection detected." ); + } + } + + protected Iterable loadServiceProvidersFor( + final Class interfaceClazz ) { + final Iterable> iterableInterfaces = loadClassesImplementing( interfaceClazz ); + return singletonContext.instantiate(iterableInterfaces); + } + + @SuppressWarnings( { "rawtypes", "unchecked" } ) + public Iterable> loadClassesImplementing( final Class interfaceClazz ) { + Iterable> implementations = (Iterable)implementedClasses.get( interfaceClazz ); + if ( implementations == null ) + synchronized ( implementedClasses ) { + implementations = (Iterable)implementedClasses.get( interfaceClazz ); + if ( implementations == null ) { + implementations = ServiceLoader.loadImplementationsFor( interfaceClazz ); + implementedClasses.put( (Class)interfaceClazz, (Iterable)implementations ); + } + } + return implementations; + } + + @Override + public void providerFor( final Class interfaceClazz, final ProducerFactory provider ) { + this.producers.memorizeProviderForClazz( provider, interfaceClazz ); + } + + @Override + public void providerFor( final Class interfaceClazz, final T object ) { + providerFor( interfaceClazz, new SingleObjectIterable( object ) ); + } + + protected void providerFor( final Class interfaceClazz, final Iterable iterable ) { + this.providers.put( interfaceClazz, iterable ); + } + + @Override + public void provideOn( final Iterable iterable ) { + for ( final T object : iterable ) + provideOn( object ); + } + + @Override + public void provideOn( final Object object ) { + try { + final ProvidableClass providableClass = retrieveProvidableClass( object.getClass() ); + providableClass.provide( object, this ); + } catch ( final Exception cause ) { + throw new ServiceProviderException( cause ); + } + } + + private ProvidableClass retrieveProvidableClass( final Class targetClazz ) { + ProvidableClass providableClass = providableClassCache.get( targetClazz ); + if ( providableClass == null ) + synchronized ( providableClassCache ) { + providableClass = providableClassCache.get( targetClazz ); + if ( providableClass == null ) { + providableClass = ProvidableClass.wrap( qualifierExtractor, targetClazz ); + providableClassCache.put( targetClazz, providableClass ); + } + } + return providableClass; + } + + private T produceFromFactory( final Class interfaceClazz, final Condition condition, final ProviderContext context ) + { + final ProducerFactory provider = getProviderFor( interfaceClazz, condition ); + if ( provider != null ) + return provider.provide( context ); + return null; + } + + @SuppressWarnings( "unchecked" ) + public ProducerFactory getProviderFor( final Class interfaceClazz, final Condition condition ) { + if ( this.producers == null ) + return null; + return (ProducerFactory)this.producers.get( interfaceClazz, condition ); + } + + @Override + public QualifierExtractor getQualifierExtractor() { + return qualifierExtractor; + } + +} diff --git a/trip-core/source/trip/spi/Producer.java b/trip-core/source/trip/spi/Producer.java index 34dd168..ba3eb4a 100644 --- a/trip-core/source/trip/spi/Producer.java +++ b/trip-core/source/trip/spi/Producer.java @@ -8,9 +8,4 @@ @Retention( RetentionPolicy.RUNTIME ) @Target( { ElementType.METHOD, ElementType.TYPE } ) public @interface Producer { - - /** - * The name that identifies the service. - */ - String name() default ""; } diff --git a/trip-core/source/trip/spi/Provided.java b/trip-core/source/trip/spi/Provided.java index e261e31..3954da6 100644 --- a/trip-core/source/trip/spi/Provided.java +++ b/trip-core/source/trip/spi/Provided.java @@ -11,11 +11,6 @@ @Target( ElementType.FIELD ) public @interface Provided { - /** - * The name that identifies the service. - */ - String name() default ""; - /** * The name that identifies the service. */ diff --git a/trip-core/source/trip/spi/ProvidedServices.java b/trip-core/source/trip/spi/ProvidedServices.java index 740e9b2..8e64afa 100644 --- a/trip-core/source/trip/spi/ProvidedServices.java +++ b/trip-core/source/trip/spi/ProvidedServices.java @@ -21,11 +21,6 @@ @Target( ElementType.FIELD ) public @interface ProvidedServices { - /** - * The name that identifies the service. - */ - String name() default ""; - /** * The name that identifies the service. */ diff --git a/trip-core/source/trip/spi/ProviderContext.java b/trip-core/source/trip/spi/ProviderContext.java index bc71cd3..7f86531 100644 --- a/trip-core/source/trip/spi/ProviderContext.java +++ b/trip-core/source/trip/spi/ProviderContext.java @@ -1,6 +1,7 @@ package trip.spi; import java.lang.annotation.Annotation; +import java.util.Collection; /** * Object holding data about and provided object. It is useful when producing @@ -11,14 +12,22 @@ public interface ProviderContext { /** * The list of annotations present on the target. - * + * * @return */ A getAnnotation( Class anntationClass ); + /** + * The list of annotations present on the target that should be used as + * filter to define which service should be injected. + * + * @return + */ + Collection> qualifierAnnotations(); + /** * The type is expected to generate an object. - * + * * @return */ Class targetType(); @@ -27,7 +36,7 @@ public interface ProviderContext { * Retrieve an attribute ( identified by {@code key} ), from current * context. Returns {@code null} if no object associated to the provided * {@code key} was found. - * + * * @param key * @return */ @@ -37,7 +46,7 @@ public interface ProviderContext { * Retrieve an attribute ( identified by {@code key} ), from current * context. Returns {@code null} if no object associated to the provided * {@code key} was found. - * + * * @param key * @return */ diff --git a/trip-core/source/trip/spi/Qualifier.java b/trip-core/source/trip/spi/Qualifier.java new file mode 100644 index 0000000..c4eb1a4 --- /dev/null +++ b/trip-core/source/trip/spi/Qualifier.java @@ -0,0 +1,11 @@ +package trip.spi; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Retention( RetentionPolicy.RUNTIME ) +@Target( { ElementType.ANNOTATION_TYPE } ) +public @interface Qualifier { +} diff --git a/trip-core/source/trip/spi/ServiceProvider.java b/trip-core/source/trip/spi/ServiceProvider.java index 2ca5eb7..86ac526 100644 --- a/trip-core/source/trip/spi/ServiceProvider.java +++ b/trip-core/source/trip/spi/ServiceProvider.java @@ -1,191 +1,30 @@ package trip.spi; -import static trip.spi.helpers.filter.Filter.filter; -import static trip.spi.helpers.filter.Filter.first; - -import java.util.HashMap; -import java.util.Map; -import java.util.ServiceConfigurationError; - -import trip.spi.helpers.EmptyProviderContext; -import trip.spi.helpers.ProducerFactoryMap; -import trip.spi.helpers.ProvidableClass; -import trip.spi.helpers.SingleObjectIterable; -import trip.spi.helpers.cache.CachedIterable; -import trip.spi.helpers.cache.ServiceLoader; -import trip.spi.helpers.filter.AnyClass; -import trip.spi.helpers.filter.AnyObject; +import trip.spi.helpers.QualifierExtractor; import trip.spi.helpers.filter.Condition; -public class ServiceProvider { - - final Map, ProvidableClass> providableClassCache = new HashMap, ProvidableClass>(); - final Map, Iterable>> implementedClasses = new HashMap, Iterable>>(); - final Map, Iterable> providers; - final ProducerFactoryMap producers; - - public ServiceProvider() { - this.providers = createDefaultProvidedData(); - runHookBeforeProducersAreReady(); - this.producers = loadAllProducers(); - runAllStartupListeners(); - } - - private void runHookBeforeProducersAreReady() { - final Iterable startupListeners = loadAll( StartupListener.class ); - for ( final StartupListener listener : startupListeners ) - listener.beforeProducersReady( this ); - } - - private void runAllStartupListeners() { - final Iterable startupListeners = loadAll( StartupListener.class ); - for ( final StartupListener listener : startupListeners ) - listener.onStartup( this ); - } - - protected Map, Iterable> createDefaultProvidedData() { - final Map, Iterable> injectables = new HashMap, Iterable>(); - injectables.put( getClass(), new SingleObjectIterable( this ) ); - return injectables; - } - - protected ProducerFactoryMap loadAllProducers() { - return ProducerFactoryMap.from( loadAll( ProducerFactory.class ) ); - } - - public T load( final Class interfaceClazz ) { - return load( interfaceClazz, new AnyObject() ); - } - - public T load( final Class interfaceClazz, final Condition condition ) { - return load( interfaceClazz, condition, new EmptyProviderContext() ); - } - - public T load( final Class interfaceClazz, final ProviderContext context ) { - return load( interfaceClazz, new AnyObject(), context ); - } - - public T load( final Class interfaceClazz, final Condition condition, final ProviderContext context ) - throws ServiceProviderException { - final T produced = produceFromFactory( interfaceClazz, condition, context ); - if ( produced != null ) - return produced; - return first( loadAll( interfaceClazz, condition ), condition ); - } - - @SuppressWarnings( "unchecked" ) - private ProducerFactory getProviderFor( final Class interfaceClazz, - final Condition condition ) { - if ( this.producers == null ) - return null; - return (ProducerFactory)this.producers.get( interfaceClazz, condition ); - } - - public Iterable loadAll( final Class interfaceClazz, final Condition condition ) { - return filter( loadAll( interfaceClazz ), condition ); - } - - @SuppressWarnings( "unchecked" ) - public Iterable loadAll( final Class interfaceClazz ) { - Iterable iterable = (Iterable)this.providers.get( interfaceClazz ); - if ( iterable == null ) - synchronized ( providers ) { - iterable = (Iterable)this.providers.get( interfaceClazz ); - if ( iterable == null ) - iterable = loadAllServicesImplementingTheInterface( interfaceClazz ); - } - return iterable; - } - - protected Iterable loadAllServicesImplementingTheInterface( final Class interfaceClazz ) { - try { - final CachedIterable iterable = loadServiceProvidersFor( interfaceClazz ); - provideOn( iterable ); - providerFor( interfaceClazz, iterable ); - return iterable; - } catch ( final StackOverflowError cause ) { - throw new ServiceConfigurationError( - "Could not load implementations of " + interfaceClazz.getCanonicalName() + - ": Recursive dependency injection detected." ); - } - } +public interface ServiceProvider { - protected CachedIterable loadServiceProvidersFor( - final Class interfaceClazz ) { - final Iterable> iterableInterfaces = loadClassesImplementing( interfaceClazz ); - return ServiceLoader.loadFrom( iterableInterfaces ); - } + T load(Class interfaceClazz); - public Class loadClassImplementing( final Class interfaceClazz ) { - return loadClassImplementing( interfaceClazz, new AnyClass() ); - } + T load(Class interfaceClazz, Condition condition); - public Class loadClassImplementing( final Class interfaceClazz, final Condition> condition ) { - return first( loadClassesImplementing( interfaceClazz ), condition ); - } + T load(Class interfaceClazz, ProviderContext context); - public Iterable> loadClassesImplementing( final Class interfaceClazz, final Condition> condition ) { - return filter( loadClassesImplementing( interfaceClazz ), condition ); - } + T load(Class interfaceClazz, Condition condition, ProviderContext context); - @SuppressWarnings( { "rawtypes", "unchecked" } ) - public Iterable> loadClassesImplementing( final Class interfaceClazz ) { - Iterable> implementations = (Iterable)implementedClasses.get( interfaceClazz ); - if ( implementations == null ) - synchronized ( implementedClasses ) { - implementations = (Iterable)implementedClasses.get( interfaceClazz ); - if ( implementations == null ) { - implementations = ServiceLoader.loadImplementationsFor( interfaceClazz ); - implementedClasses.put( (Class)interfaceClazz, (Iterable)implementations ); - } - } - return implementations; - } + Iterable loadAll(Class interfaceClazz, Condition condition); - public void providerFor( final Class interfaceClazz, final ProducerFactory provider ) { - this.producers.memorizeProviderForClazz( provider, interfaceClazz ); - } + Iterable loadAll(Class interfaceClazz); - public void providerFor( final Class interfaceClazz, final T object ) { - providerFor( interfaceClazz, new SingleObjectIterable( object ) ); - } + void providerFor(Class interfaceClazz, ProducerFactory provider); - protected void providerFor( final Class interfaceClazz, final Iterable iterable ) { - this.providers.put( interfaceClazz, iterable ); - } + void providerFor(Class interfaceClazz, T object); - public void provideOn( final Iterable iterable ) { - for ( final T object : iterable ) - provideOn( object ); - } + void provideOn(Iterable iterable); - public void provideOn( final Object object ) { - try { - final ProvidableClass providableClass = retrieveProvidableClass( object.getClass() ); - providableClass.provide( object, this ); - } catch ( final Exception cause ) { - throw new ServiceProviderException( cause ); - } - } + void provideOn(Object object); - private ProvidableClass retrieveProvidableClass( final Class targetClazz ) { - ProvidableClass providableClass = providableClassCache.get( targetClazz ); - if ( providableClass == null ) - synchronized ( providableClassCache ) { - providableClass = providableClassCache.get( targetClazz ); - if ( providableClass == null ) { - providableClass = ProvidableClass.wrap( targetClazz ); - providableClassCache.put( targetClazz, providableClass ); - } - } - return providableClass; - } + QualifierExtractor getQualifierExtractor(); - private T produceFromFactory( final Class interfaceClazz, final Condition condition, final ProviderContext context ) - { - final ProducerFactory provider = getProviderFor( interfaceClazz, condition ); - if ( provider != null ) - return provider.provide( context ); - return null; - } -} +} \ No newline at end of file diff --git a/trip-core/source/trip/spi/Singleton.java b/trip-core/source/trip/spi/Singleton.java index 2d84821..04ce3ff 100644 --- a/trip-core/source/trip/spi/Singleton.java +++ b/trip-core/source/trip/spi/Singleton.java @@ -14,9 +14,4 @@ public @interface Singleton { Class exposedAs() default Singleton.class; - - /** - * The name that identifies the service. - */ - String name() default ""; } diff --git a/trip-core/source/trip/spi/SingletonContext.java b/trip-core/source/trip/spi/SingletonContext.java new file mode 100644 index 0000000..e4ddd03 --- /dev/null +++ b/trip-core/source/trip/spi/SingletonContext.java @@ -0,0 +1,39 @@ +package trip.spi; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import lombok.RequiredArgsConstructor; +import lombok.extern.java.Log; + +@RequiredArgsConstructor +@Log +class SingletonContext { + + final Map, Object> cache = new HashMap<>(); + + @SuppressWarnings("unchecked") + public Iterable instantiate( Iterable> classes ){ + final List list = new ArrayList<>(); + for ( final Class clazz : classes ){ + T object = (T)cache.get(clazz); + if ( object == null ) + object = instantiate(clazz); + list.add(object); + } + return list; + } + + T instantiate( Class clazz ) { + try { + final T instance = clazz.newInstance(); + cache.put( clazz, instance); + return instance; + } catch ( final IllegalAccessException | InstantiationException cause ) { + log.warning( cause.getMessage() ); + throw new IllegalStateException( cause ); + } + } +} \ No newline at end of file diff --git a/trip-core/source/trip/spi/StartupListener.java b/trip-core/source/trip/spi/StartupListener.java index 445139d..3bcf134 100644 --- a/trip-core/source/trip/spi/StartupListener.java +++ b/trip-core/source/trip/spi/StartupListener.java @@ -4,5 +4,5 @@ public interface StartupListener { default void beforeProducersReady( final ServiceProvider provider ){} - void onStartup( final ServiceProvider provider ); + void onStartup( final DefaultServiceProvider provider ); } diff --git a/trip-core/source/trip/spi/Stateless.java b/trip-core/source/trip/spi/Stateless.java index fcc2d2b..673f9fd 100644 --- a/trip-core/source/trip/spi/Stateless.java +++ b/trip-core/source/trip/spi/Stateless.java @@ -12,9 +12,4 @@ public @interface Stateless { Class exposedAs() default Stateless.class; - - /** - * The name that identifies the service. - */ - String name() default ""; } diff --git a/trip-core/source/trip/spi/helpers/DefaultFieldQualifierExtractor.java b/trip-core/source/trip/spi/helpers/DefaultFieldQualifierExtractor.java new file mode 100644 index 0000000..dce9faa --- /dev/null +++ b/trip-core/source/trip/spi/helpers/DefaultFieldQualifierExtractor.java @@ -0,0 +1,13 @@ +package trip.spi.helpers; + +import java.lang.annotation.Annotation; + +import trip.spi.Qualifier; + +public class DefaultFieldQualifierExtractor implements FieldQualifierExtractor { + + @Override + public boolean isAnnotatedWithQualifierAnnotation(Class ann) { + return ann.isAnnotationPresent(Qualifier.class); + } +} diff --git a/trip-core/source/trip/spi/helpers/EmptyProviderContext.java b/trip-core/source/trip/spi/helpers/EmptyProviderContext.java index 0a4db07..69ac3e0 100644 --- a/trip-core/source/trip/spi/helpers/EmptyProviderContext.java +++ b/trip-core/source/trip/spi/helpers/EmptyProviderContext.java @@ -1,11 +1,15 @@ package trip.spi.helpers; import java.lang.annotation.Annotation; +import java.util.Collection; +import java.util.Collections; import trip.spi.ProviderContext; public class EmptyProviderContext implements ProviderContext { + public final static ProviderContext INSTANCE = new EmptyProviderContext(); + @Override public A getAnnotation( Class anntationClass ) { return null; @@ -25,4 +29,9 @@ public Object attribute( String key ) { public T attribute( Class key ) { return null; } + + @Override + public Collection> qualifierAnnotations() { + return Collections.emptyList(); + } } diff --git a/trip-core/source/trip/spi/helpers/FieldProviderContext.java b/trip-core/source/trip/spi/helpers/FieldProviderContext.java index f6a48df..12af2f6 100644 --- a/trip-core/source/trip/spi/helpers/FieldProviderContext.java +++ b/trip-core/source/trip/spi/helpers/FieldProviderContext.java @@ -2,6 +2,7 @@ import java.lang.annotation.Annotation; import java.lang.reflect.Field; +import java.util.Collection; import lombok.Getter; import lombok.RequiredArgsConstructor; @@ -12,10 +13,11 @@ * Holds data about a field some value should be injected. */ @Getter -@Accessors +@Accessors(fluent=true) @RequiredArgsConstructor public class FieldProviderContext implements ProviderContext { + final Collection> qualifierAnnotations; final Field field; @Override diff --git a/trip-core/source/trip/spi/helpers/FieldQualifierExtractor.java b/trip-core/source/trip/spi/helpers/FieldQualifierExtractor.java new file mode 100644 index 0000000..bfd92cf --- /dev/null +++ b/trip-core/source/trip/spi/helpers/FieldQualifierExtractor.java @@ -0,0 +1,19 @@ +package trip.spi.helpers; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.List; + +public interface FieldQualifierExtractor { + + default List> extractQualifiersFrom(Field field) { + final List> anns = new ArrayList<>(); + for ( final Annotation ann : field.getAnnotations() ) + if ( isAnnotatedWithQualifierAnnotation( ann.annotationType() ) ) + anns.add( ann.annotationType() ); + return anns; + } + + boolean isAnnotatedWithQualifierAnnotation(Class ann); +} diff --git a/trip-core/source/trip/spi/helpers/KeyValueProviderContext.java b/trip-core/source/trip/spi/helpers/KeyValueProviderContext.java index cf6c6b8..f2c6a05 100644 --- a/trip-core/source/trip/spi/helpers/KeyValueProviderContext.java +++ b/trip-core/source/trip/spi/helpers/KeyValueProviderContext.java @@ -1,10 +1,14 @@ package trip.spi.helpers; import java.lang.annotation.Annotation; +import java.util.Collection; +import java.util.Collections; import java.util.HashMap; import java.util.Map; -import lombok.*; +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import lombok.Setter; import lombok.experimental.Accessors; import trip.spi.ProviderContext; @@ -50,4 +54,9 @@ public Object attribute( String key ) { public T attribute( Class key ) { return (T)attribute( key.getCanonicalName() ); } + + @Override + public Collection> qualifierAnnotations() { + return Collections.emptyList(); + } } diff --git a/trip-core/source/trip/spi/helpers/ManyElementsProvidableField.java b/trip-core/source/trip/spi/helpers/ManyElementsProvidableField.java index 7ec5173..9906939 100644 --- a/trip-core/source/trip/spi/helpers/ManyElementsProvidableField.java +++ b/trip-core/source/trip/spi/helpers/ManyElementsProvidableField.java @@ -1,16 +1,19 @@ package trip.spi.helpers; +import java.lang.annotation.Annotation; import java.lang.reflect.Field; +import java.util.Collection; import lombok.RequiredArgsConstructor; import lombok.val; import trip.spi.ProvidedServices; import trip.spi.ServiceProvider; import trip.spi.ServiceProviderException; -import trip.spi.helpers.filter.AnyObject; import trip.spi.helpers.filter.Condition; +import trip.spi.helpers.filter.QualifierCondition; @RequiredArgsConstructor +@SuppressWarnings( { "unchecked" } ) public class ManyElementsProvidableField implements ProvidableField { final Field field; @@ -28,17 +31,16 @@ public void set( final Object instance, final Object value ) throws IllegalArgum field.set( instance, value ); } - @SuppressWarnings( { "unchecked" } ) - public static ProvidableField from( final Field field ) { + public static ProvidableField from( Collection> qualifiers, final Field field ) { assertFieldTypeIsIterable( field ); field.setAccessible( true ); val provided = field.getAnnotation( ProvidedServices.class ); return new ManyElementsProvidableField( field, (Class)provided.exposedAs(), - new AnyObject() ); + (Condition)new QualifierCondition<>(qualifiers) ); } - static void assertFieldTypeIsIterable( final Field field ) { + private static void assertFieldTypeIsIterable( final Field field ) { if ( !Iterable.class.equals( field.getType() ) ) throw new IllegalStateException( "Field " + field.getName() + " expects to have Iterable type." ); } diff --git a/trip-core/source/trip/spi/helpers/ProvidableClass.java b/trip-core/source/trip/spi/helpers/ProvidableClass.java index 45ce0e0..488f512 100644 --- a/trip-core/source/trip/spi/helpers/ProvidableClass.java +++ b/trip-core/source/trip/spi/helpers/ProvidableClass.java @@ -1,7 +1,10 @@ package trip.spi.helpers; +import java.lang.annotation.Annotation; import java.lang.reflect.Field; import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; import java.util.List; import lombok.RequiredArgsConstructor; @@ -19,19 +22,19 @@ public class ProvidableClass { public void provide( Object instance, ServiceProvider provider ) throws ServiceProviderException, IllegalArgumentException, IllegalAccessException { - for ( ProvidableField field : fields ) + for ( final ProvidableField field : fields ) field.provide( instance, provider ); } - public static ProvidableClass wrap( Class targetClazz ) { - return new ProvidableClass( targetClazz, readClassProvidableFields( targetClazz ) ); + public static ProvidableClass wrap( QualifierExtractor extractor, Class targetClazz ) { + return new ProvidableClass( targetClazz, readClassProvidableFields( extractor, targetClazz ) ); } - static Iterable readClassProvidableFields( Class targetClazz ) { - List providableFields = new ArrayList(); + static Iterable readClassProvidableFields( QualifierExtractor extractor, Class targetClazz ) { + final List providableFields = new ArrayList(); Class clazz = targetClazz; while ( !Object.class.equals( clazz ) ) { - populateWithProvidableFields( clazz, providableFields ); + populateWithProvidableFields( extractor, clazz, providableFields ); if ( clazz.isAnnotationPresent(GeneratedFromStatelessService.class) ) break; clazz = clazz.getSuperclass(); @@ -39,11 +42,21 @@ static Iterable readClassProvidableFields( Class targetClazz return providableFields; } - static void populateWithProvidableFields( Class targetClazz, List providableFields ) { - for ( Field field : targetClazz.getDeclaredFields() ) + static void populateWithProvidableFields( QualifierExtractor extractor, Class targetClazz, List providableFields ) { + for ( final Field field : targetClazz.getDeclaredFields() ){ + final Collection> qualifiers = extractQualifiersFromAvoidingNPEWhenCreatingQualifierExtractor(extractor, field); if ( field.isAnnotationPresent( Provided.class ) ) - providableFields.add( SingleElementProvidableField.from( field ) ); + providableFields.add( SingleElementProvidableField.from( qualifiers, field ) ); else if ( field.isAnnotationPresent( ProvidedServices.class ) ) - providableFields.add( ManyElementsProvidableField.from( field ) ); + providableFields.add( ManyElementsProvidableField.from( qualifiers, field ) ); + } + } + + private static Collection> extractQualifiersFromAvoidingNPEWhenCreatingQualifierExtractor( + final QualifierExtractor extractor, final Field field) + { + if ( null == extractor ) + return Collections.emptyList(); + return extractor.extractQualifiersFrom(field); } } diff --git a/trip-core/source/trip/spi/helpers/QualifierExtractor.java b/trip-core/source/trip/spi/helpers/QualifierExtractor.java new file mode 100644 index 0000000..68d3145 --- /dev/null +++ b/trip-core/source/trip/spi/helpers/QualifierExtractor.java @@ -0,0 +1,28 @@ +package trip.spi.helpers; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.Collection; + +import lombok.RequiredArgsConstructor; + +@RequiredArgsConstructor +public class QualifierExtractor { + + final Iterable extractors; + + public Collection> extractQualifiersFrom( Field field ){ + final Collection> anns = new ArrayList<>(); + for ( final FieldQualifierExtractor extractor : extractors ) + anns.addAll( extractor.extractQualifiersFrom(field) ); + return anns; + } + + public boolean isAnnotatedWithQualifierAnnotation( Class ann ){ + for ( final FieldQualifierExtractor extractor : extractors ) + if ( extractor.isAnnotatedWithQualifierAnnotation(ann) ) + return true; + return false; + } +} diff --git a/trip-core/source/trip/spi/helpers/SingleElementProvidableField.java b/trip-core/source/trip/spi/helpers/SingleElementProvidableField.java index da453cb..b636648 100644 --- a/trip-core/source/trip/spi/helpers/SingleElementProvidableField.java +++ b/trip-core/source/trip/spi/helpers/SingleElementProvidableField.java @@ -1,6 +1,8 @@ package trip.spi.helpers; +import java.lang.annotation.Annotation; import java.lang.reflect.Field; +import java.util.Collection; import lombok.Value; import lombok.extern.java.Log; @@ -8,11 +10,14 @@ import trip.spi.ProviderContext; import trip.spi.ServiceProvider; import trip.spi.ServiceProviderException; +import trip.spi.helpers.filter.ChainedCondition; import trip.spi.helpers.filter.Condition; import trip.spi.helpers.filter.IsAssignableFrom; +import trip.spi.helpers.filter.QualifierCondition; @Log @Value +@SuppressWarnings( { "unchecked", "rawtypes" } ) public class SingleElementProvidableField implements ProvidableField { final Field field; @@ -33,15 +38,21 @@ public void set( final Object instance, final Object value ) throws IllegalArgum field.set( instance, value ); } - @SuppressWarnings( { "unchecked", "rawtypes" } ) - public static ProvidableField from( final Field field ) { + public static ProvidableField from( Collection> qualifiers, final Field field ) { field.setAccessible( true ); final Provided provided = field.getAnnotation( Provided.class ); final Class expectedClass = provided.exposedAs().equals( Provided.class ) ? field.getType() : provided.exposedAs(); return new SingleElementProvidableField( field, (Class)expectedClass, - (Condition)new IsAssignableFrom( field.getType() ), - new FieldProviderContext( field ) ); + createInjectionCondition( qualifiers, field), + new FieldProviderContext( qualifiers, field ) ); + } + + private static Condition createInjectionCondition(Collection> qualifiers, final Field field) { + final ChainedCondition condition = new ChainedCondition<>(); + condition.add((Condition)new IsAssignableFrom( field.getType() )); + condition.add((Condition)new QualifierCondition<>(qualifiers)); + return condition; } } diff --git a/trip-core/source/trip/spi/helpers/cache/LazyClassInstantor.java b/trip-core/source/trip/spi/helpers/cache/LazyClassInstantor.java deleted file mode 100644 index fa929fe..0000000 --- a/trip-core/source/trip/spi/helpers/cache/LazyClassInstantor.java +++ /dev/null @@ -1,44 +0,0 @@ -package trip.spi.helpers.cache; - -import java.util.ArrayList; -import java.util.Iterator; -import java.util.List; - -import lombok.Getter; -import lombok.RequiredArgsConstructor; -import lombok.extern.java.Log; - -@Log -@Getter -@RequiredArgsConstructor -public class LazyClassInstantor implements Iterator { - - final Iterator> reader; - List cache = new ArrayList(); - - @Override - public boolean hasNext() { - return reader.hasNext(); - } - - @Override - public T next() { - try { - final Class clazz = reader.next(); - final T instance = clazz.newInstance(); - cache.add(instance); - return instance; - } catch ( final IllegalAccessException cause ) { - log.warning( cause.getMessage() ); - throw new IllegalStateException( cause ); - } catch ( final InstantiationException cause ) { - log.warning( cause.getMessage() ); - throw new IllegalStateException(cause); - } - } - - @Override - public void remove() { - reader.remove(); - } -} diff --git a/trip-core/source/trip/spi/helpers/cache/LazyClassReader.java b/trip-core/source/trip/spi/helpers/cache/LazyClassReader.java index 1b3eb12..236ccb8 100644 --- a/trip-core/source/trip/spi/helpers/cache/LazyClassReader.java +++ b/trip-core/source/trip/spi/helpers/cache/LazyClassReader.java @@ -88,12 +88,8 @@ public void remove() { } Iterator readLines( final URL url ) throws IOException { - @Cleanup - final - InputStream inputStream = url.openStream(); - @Cleanup - final - BufferedReader reader = new BufferedReader( new InputStreamReader( inputStream, "utf-8" ) ); + @Cleanup final InputStream inputStream = url.openStream(); + @Cleanup final BufferedReader reader = new BufferedReader( new InputStreamReader( inputStream, "utf-8" ) ); final List lines = new ArrayList(); String line = null; while ( ( line = readNextLine( reader ) ) != null ) diff --git a/trip-core/source/trip/spi/helpers/cache/ServiceLoader.java b/trip-core/source/trip/spi/helpers/cache/ServiceLoader.java index 0be649b..acd23d3 100644 --- a/trip-core/source/trip/spi/helpers/cache/ServiceLoader.java +++ b/trip-core/source/trip/spi/helpers/cache/ServiceLoader.java @@ -2,25 +2,17 @@ import java.util.Iterator; -import lombok.RequiredArgsConstructor; +public abstract class ServiceLoader { -@RequiredArgsConstructor -public class ServiceLoader { - public static CachedIterable> loadImplementationsFor( Class clazz ) { - ClassLoader cl = Thread.currentThread().getContextClassLoader(); - Iterator> reader = new LazyClassReader(clazz, cl); + final ClassLoader cl = Thread.currentThread().getContextClassLoader(); + final Iterator> reader = new LazyClassReader(clazz, cl); return new CachedIterable>(reader); } public static CachedIterable> loadImplementationsFor( String interfaceCanonicalName ) { - ClassLoader cl = Thread.currentThread().getContextClassLoader(); - Iterator> reader = new LazyClassReader( interfaceCanonicalName, cl ); + final ClassLoader cl = Thread.currentThread().getContextClassLoader(); + final Iterator> reader = new LazyClassReader( interfaceCanonicalName, cl ); return new CachedIterable>( reader ); } - - public static CachedIterable loadFrom( Iterable> interfaces ) { - Iterator instantor = new LazyClassInstantor(interfaces.iterator()); - return new CachedIterable(instantor); - } } diff --git a/trip-core/source/trip/spi/helpers/filter/QualifierCondition.java b/trip-core/source/trip/spi/helpers/filter/QualifierCondition.java new file mode 100644 index 0000000..ba85fbb --- /dev/null +++ b/trip-core/source/trip/spi/helpers/filter/QualifierCondition.java @@ -0,0 +1,21 @@ +package trip.spi.helpers.filter; + +import java.lang.annotation.Annotation; +import java.util.Collection; + +import lombok.RequiredArgsConstructor; + +@RequiredArgsConstructor +public class QualifierCondition implements Condition { + + final Collection> qualifiers; + + @Override + public boolean check(T object) { + final Class targetClass = object.getClass(); + for ( final Class ann: qualifiers ) + if (!targetClass.isAnnotationPresent(ann)) + return false; + return true; + } +} diff --git a/trip-core/tests/trip/spi/ClosureProvider.java b/trip-core/tests/trip/spi/ClosureProvider.java index 10daf50..08cf1be 100644 --- a/trip-core/tests/trip/spi/ClosureProvider.java +++ b/trip-core/tests/trip/spi/ClosureProvider.java @@ -1,6 +1,7 @@ package trip.spi; -@Singleton( name = "period" ) +@Singleton +@Period public class ClosureProvider implements ProducerFactory { @Override @@ -9,11 +10,9 @@ public Closure provide( ProviderContext context ) { } class PeriodClosure implements Closure { - @Override public Character getSentenceClosureChar() { return Closure.PERIOD; } - } } diff --git a/trip-core/tests/trip/spi/Foo.java b/trip-core/tests/trip/spi/Foo.java new file mode 100644 index 0000000..c445e60 --- /dev/null +++ b/trip-core/tests/trip/spi/Foo.java @@ -0,0 +1,11 @@ +package trip.spi; + +import java.lang.annotation.RetentionPolicy; + +import java.lang.annotation.Retention; + +@Qualifier +@Retention(RetentionPolicy.RUNTIME) +public @interface Foo { + +} diff --git a/trip-core/tests/trip/spi/InheritedStatelessServiceTest.java b/trip-core/tests/trip/spi/InheritedStatelessServiceTest.java index 431d6c7..3ef26ee 100644 --- a/trip-core/tests/trip/spi/InheritedStatelessServiceTest.java +++ b/trip-core/tests/trip/spi/InheritedStatelessServiceTest.java @@ -12,7 +12,7 @@ public class InheritedStatelessServiceTest { @Test @SneakyThrows public void ensureThatStatelessServiceDoesntHaveSuperClassDependenciesProvided() { - val provider = new ServiceProvider(); + val provider = new DefaultServiceProvider(); val fakeService = (MyFakeStatelessService)provider.load( Readable.class ); val superclassProvidedObject = fakeService.getWord(); assertNull( superclassProvidedObject ); @@ -21,7 +21,7 @@ public void ensureThatStatelessServiceDoesntHaveSuperClassDependenciesProvided() @Test @SneakyThrows public void ensureThatStatelessServiceCouldProvideDataInChildClass() { - val provider = new ServiceProvider(); + val provider = new DefaultServiceProvider(); val fakeService = (MyFakeStatelessService)provider.load( Readable.class ); val childclassProvidedObject = fakeService.getPrintable(); assertNotNull( childclassProvidedObject ); diff --git a/trip-core/tests/trip/spi/Period.java b/trip-core/tests/trip/spi/Period.java new file mode 100644 index 0000000..cdf910e --- /dev/null +++ b/trip-core/tests/trip/spi/Period.java @@ -0,0 +1,10 @@ +package trip.spi; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +@Qualifier +@Retention(RetentionPolicy.RUNTIME) +public @interface Period { + +} diff --git a/trip-core/tests/trip/spi/PrintableFoo.java b/trip-core/tests/trip/spi/PrintableFoo.java index 6899572..af5a622 100644 --- a/trip-core/tests/trip/spi/PrintableFoo.java +++ b/trip-core/tests/trip/spi/PrintableFoo.java @@ -1,6 +1,7 @@ package trip.spi; -@Singleton( name = "foo" ) +@Foo +@Singleton public class PrintableFoo implements PrintableWord { @Override diff --git a/trip-core/tests/trip/spi/PrintableWorld.java b/trip-core/tests/trip/spi/PrintableWorld.java index 7ecd626..b20ab6c 100644 --- a/trip-core/tests/trip/spi/PrintableWorld.java +++ b/trip-core/tests/trip/spi/PrintableWorld.java @@ -5,7 +5,8 @@ @Getter public class PrintableWorld implements PrintableWord { - @Provided( name = "period" ) + @Period + @Provided Closure closure; @Override diff --git a/trip-core/tests/trip/spi/ProviderTest.java b/trip-core/tests/trip/spi/ProviderTest.java index e6933aa..12bb041 100644 --- a/trip-core/tests/trip/spi/ProviderTest.java +++ b/trip-core/tests/trip/spi/ProviderTest.java @@ -11,7 +11,7 @@ public class ProviderTest { - final ServiceProvider provider = new ServiceProvider(); + final DefaultServiceProvider provider = new DefaultServiceProvider(); @Before public void grantThatProvidedHasNoCachedData() { diff --git a/trip-core/tests/trip/spi/SingleAndManyServicesInjectionTest.java b/trip-core/tests/trip/spi/SingleAndManyServicesInjectionTest.java index 35fc8f7..6926206 100644 --- a/trip-core/tests/trip/spi/SingleAndManyServicesInjectionTest.java +++ b/trip-core/tests/trip/spi/SingleAndManyServicesInjectionTest.java @@ -11,7 +11,7 @@ public class SingleAndManyServicesInjectionTest { - static final ServiceProvider provider = new ServiceProvider(); + static final ServiceProvider provider = new DefaultServiceProvider(); @Test( timeout = 3000 ) public void applyStressTestOnMyAssertion() throws ServiceProviderException { @@ -65,6 +65,7 @@ class InjectableClass { @ProvidedServices( exposedAs = PrintableWord.class ) Iterable printables; - @ProvidedServices( exposedAs = PrintableWord.class, name = "foo" ) + @Foo + @ProvidedServices( exposedAs = PrintableWord.class ) Iterable printableFoos; } \ No newline at end of file diff --git a/trip-core/tests/trip/spi/SuperclassInjectionTest.java b/trip-core/tests/trip/spi/SuperclassInjectionTest.java index d0ba321..81169ad 100644 --- a/trip-core/tests/trip/spi/SuperclassInjectionTest.java +++ b/trip-core/tests/trip/spi/SuperclassInjectionTest.java @@ -9,7 +9,7 @@ public class SuperclassInjectionTest { @Test public void ensureThatInjectedOnSuperclass() throws ServiceProviderException { - final ServiceProvider provider = new ServiceProvider(); + final ServiceProvider provider = new DefaultServiceProvider(); final MyPrintable printable = new MyPrintable(); provider.provideOn( printable ); assertNotNull( printable.printableWord ); diff --git a/trip-core/tests/trip/spi/helpers/QualifierConditionTest.java b/trip-core/tests/trip/spi/helpers/QualifierConditionTest.java new file mode 100644 index 0000000..9390bcd --- /dev/null +++ b/trip-core/tests/trip/spi/helpers/QualifierConditionTest.java @@ -0,0 +1,24 @@ +package trip.spi.helpers; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import java.util.Arrays; + +import org.junit.Test; + +import trip.spi.Foo; +import trip.spi.PrintableFoo; +import trip.spi.PrintableWorld; +import trip.spi.helpers.filter.Condition; +import trip.spi.helpers.filter.QualifierCondition; + +public class QualifierConditionTest { + + @Test + public void ensureThatCanRetrieveOnlyQualifiedElements(){ + final Condition condition = new QualifierCondition(Arrays.asList(Foo.class)); + assertFalse( condition.check( new PrintableWorld() ) ); + assertTrue( condition.check(new PrintableFoo()) ); + } +} diff --git a/trip-core/tests/trip/spi/helpers/QualifierExtractorTest.java b/trip-core/tests/trip/spi/helpers/QualifierExtractorTest.java new file mode 100644 index 0000000..5668a5c --- /dev/null +++ b/trip-core/tests/trip/spi/helpers/QualifierExtractorTest.java @@ -0,0 +1,51 @@ +package trip.spi.helpers; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import java.lang.annotation.Annotation; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.reflect.Field; +import java.util.Arrays; +import java.util.Collection; + +import lombok.SneakyThrows; + +import org.junit.Before; +import org.junit.Test; + +import trip.spi.Qualifier; + +public class QualifierExtractorTest { + + QualifierExtractor extractor; + + @MyQualifiedAnn + String qualifiedString; + + @Before + public void setup(){ + extractor = new QualifierExtractor( Arrays.asList( new DefaultFieldQualifierExtractor() ) ); + } + + @Test + @SneakyThrows + public void ensureThatCanFindAnnotationsWithDefaultQualifier(){ + final Field field = QualifierExtractorTest.class.getDeclaredField("qualifiedString"); + final Collection> qualifiers = extractor.extractQualifiersFrom(field); + for ( final Class ann : qualifiers ) + assertEquals( MyQualifiedAnn.class, ann ); + } + + @Test + public void ensureThatCanDetecteQualifierAnnotations(){ + assertTrue( extractor.isAnnotatedWithQualifierAnnotation(MyQualifiedAnn.class) ); + } +} + +@Qualifier +@Retention(RetentionPolicy.RUNTIME) +@interface MyQualifiedAnn { + +} \ No newline at end of file diff --git a/trip-core/tests/trip/spi/startup/ConfigurationStartupListener.java b/trip-core/tests/trip/spi/startup/ConfigurationStartupListener.java index 8da7e6e..78c59a2 100644 --- a/trip-core/tests/trip/spi/startup/ConfigurationStartupListener.java +++ b/trip-core/tests/trip/spi/startup/ConfigurationStartupListener.java @@ -1,6 +1,6 @@ package trip.spi.startup; -import trip.spi.ServiceProvider; +import trip.spi.DefaultServiceProvider; import trip.spi.StartupListener; public class ConfigurationStartupListener implements StartupListener { @@ -8,7 +8,7 @@ public class ConfigurationStartupListener implements StartupListener { public static final String EXPECTED_CONFIG = "I was Injected"; @Override - public void onStartup( final ServiceProvider provider ) { + public void onStartup( final DefaultServiceProvider provider ) { provider.providerFor( Configuration.class, new Configuration( EXPECTED_CONFIG ) ); } } diff --git a/trip-core/tests/trip/spi/startup/StartupListenerTest.java b/trip-core/tests/trip/spi/startup/StartupListenerTest.java index de3e7c3..76421e0 100644 --- a/trip-core/tests/trip/spi/startup/StartupListenerTest.java +++ b/trip-core/tests/trip/spi/startup/StartupListenerTest.java @@ -9,6 +9,7 @@ import org.junit.Test; import trip.spi.Provided; +import trip.spi.DefaultServiceProvider; import trip.spi.ServiceProvider; public class StartupListenerTest { @@ -26,7 +27,7 @@ public void ensureThatConfigurationWasInjectedByStartupListenerAsExpected() { @Before @SneakyThrows public void provideDependencies() { - final ServiceProvider provider = new ServiceProvider(); + final ServiceProvider provider = new DefaultServiceProvider(); provider.provideOn( this ); } } diff --git a/trip-integration-tests/source/trip/spi/tests/AjaxFromMars.java b/trip-integration-tests/source/trip/spi/tests/AjaxFromMars.java index 1d2cb3f..f4b46aa 100644 --- a/trip-integration-tests/source/trip/spi/tests/AjaxFromMars.java +++ b/trip-integration-tests/source/trip/spi/tests/AjaxFromMars.java @@ -2,8 +2,10 @@ import trip.spi.Provided; import trip.spi.Singleton; +import trip.spi.tests.ann.Ajax; -@Singleton( exposedAs = Hero.class, name = "ajax" ) +@Ajax +@Singleton( exposedAs = Hero.class ) public class AjaxFromMars implements Hero, World { @Provided diff --git a/trip-integration-tests/source/trip/spi/tests/Batman.java b/trip-integration-tests/source/trip/spi/tests/Batman.java index ef4e497..7992862 100644 --- a/trip-integration-tests/source/trip/spi/tests/Batman.java +++ b/trip-integration-tests/source/trip/spi/tests/Batman.java @@ -2,8 +2,10 @@ import trip.spi.Provided; import trip.spi.Singleton; +import trip.spi.tests.ann.DarkKnight; -@Singleton( exposedAs = Hero.class, name = "batman" ) +@DarkKnight +@Singleton( exposedAs = Hero.class ) public class Batman implements Hero, World { @Provided( exposedAs = World.class ) diff --git a/trip-integration-tests/source/trip/spi/tests/HelloWorldProvider.java b/trip-integration-tests/source/trip/spi/tests/HelloWorldProvider.java index b5ae41a..3869ae3 100644 --- a/trip-integration-tests/source/trip/spi/tests/HelloWorldProvider.java +++ b/trip-integration-tests/source/trip/spi/tests/HelloWorldProvider.java @@ -1,6 +1,7 @@ package trip.spi.tests; import trip.spi.Producer; +import trip.spi.tests.ann.Foo; public class HelloWorldProvider { @@ -9,7 +10,8 @@ public HelloWorld createHelloWorld() { return new HelloWorld(); } - @Producer( name = "foo" ) + @Producer + @Foo public HelloWorld createHelloFooo() { return new HelloFoo(); } diff --git a/trip-integration-tests/source/trip/spi/tests/ann/Ajax.java b/trip-integration-tests/source/trip/spi/tests/ann/Ajax.java new file mode 100644 index 0000000..ac0184f --- /dev/null +++ b/trip-integration-tests/source/trip/spi/tests/ann/Ajax.java @@ -0,0 +1,11 @@ +package trip.spi.tests.ann; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +import trip.spi.Qualifier; + +@Qualifier +@Retention(RetentionPolicy.RUNTIME) +public @interface Ajax { +} diff --git a/trip-integration-tests/source/trip/spi/tests/ann/DarkKnight.java b/trip-integration-tests/source/trip/spi/tests/ann/DarkKnight.java new file mode 100644 index 0000000..dd96290 --- /dev/null +++ b/trip-integration-tests/source/trip/spi/tests/ann/DarkKnight.java @@ -0,0 +1,11 @@ +package trip.spi.tests.ann; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +import trip.spi.Qualifier; + +@Qualifier +@Retention(RetentionPolicy.RUNTIME) +public @interface DarkKnight { +} diff --git a/trip-integration-tests/source/trip/spi/tests/ann/Foo.java b/trip-integration-tests/source/trip/spi/tests/ann/Foo.java new file mode 100644 index 0000000..f586f1a --- /dev/null +++ b/trip-integration-tests/source/trip/spi/tests/ann/Foo.java @@ -0,0 +1,11 @@ +package trip.spi.tests.ann; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +import trip.spi.Qualifier; + +@Qualifier +@Retention(RetentionPolicy.RUNTIME) +public @interface Foo { +} diff --git a/trip-integration-tests/source/trip/spi/tests/ann/Names.java b/trip-integration-tests/source/trip/spi/tests/ann/Names.java new file mode 100644 index 0000000..c4c6934 --- /dev/null +++ b/trip-integration-tests/source/trip/spi/tests/ann/Names.java @@ -0,0 +1,11 @@ +package trip.spi.tests.ann; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +import trip.spi.Qualifier; + +@Qualifier +@Retention(RetentionPolicy.RUNTIME) +public @interface Names { +} diff --git a/trip-integration-tests/source/trip/spi/tests/concurrency/NameProducer.java b/trip-integration-tests/source/trip/spi/tests/concurrency/NameProducer.java index 714b288..5516888 100644 --- a/trip-integration-tests/source/trip/spi/tests/concurrency/NameProducer.java +++ b/trip-integration-tests/source/trip/spi/tests/concurrency/NameProducer.java @@ -6,11 +6,13 @@ import lombok.val; import trip.spi.Producer; import trip.spi.Singleton; +import trip.spi.tests.ann.Names; @Singleton public class NameProducer { - @Producer( name = "names" ) + @Producer + @Names public List produceNames() { val list = new ArrayList(); list.add( "Ereim" ); diff --git a/trip-integration-tests/source/trip/spi/tests/concurrency/PrinterRunner.java b/trip-integration-tests/source/trip/spi/tests/concurrency/PrinterRunner.java index f14502c..63b1d84 100644 --- a/trip-integration-tests/source/trip/spi/tests/concurrency/PrinterRunner.java +++ b/trip-integration-tests/source/trip/spi/tests/concurrency/PrinterRunner.java @@ -4,14 +4,14 @@ import java.util.concurrent.CountDownLatch; import lombok.RequiredArgsConstructor; -import trip.spi.ServiceProvider; +import trip.spi.DefaultServiceProvider; import trip.spi.ServiceProviderException; @RequiredArgsConstructor public class PrinterRunner implements Runnable { final BlockingQueue events; - final ServiceProvider provider; + final DefaultServiceProvider provider; final CountDownLatch couter; @Override diff --git a/trip-integration-tests/source/trip/spi/tests/concurrency/StatelessService.java b/trip-integration-tests/source/trip/spi/tests/concurrency/StatelessService.java index 4bac301..eb81526 100644 --- a/trip-integration-tests/source/trip/spi/tests/concurrency/StatelessService.java +++ b/trip-integration-tests/source/trip/spi/tests/concurrency/StatelessService.java @@ -5,11 +5,13 @@ import lombok.val; import trip.spi.Provided; import trip.spi.Stateless; +import trip.spi.tests.ann.Names; @Stateless public class StatelessService { - @Provided( name = "names" ) + @Provided + @Names List names; @Provided diff --git a/trip-integration-tests/tests/trip/spi/tests/ConcurrencyTest.java b/trip-integration-tests/tests/trip/spi/tests/ConcurrencyTest.java index ad0fb87..7ed42b6 100644 --- a/trip-integration-tests/tests/trip/spi/tests/ConcurrencyTest.java +++ b/trip-integration-tests/tests/trip/spi/tests/ConcurrencyTest.java @@ -12,13 +12,13 @@ import org.junit.After; import org.junit.Test; -import trip.spi.ServiceProvider; +import trip.spi.DefaultServiceProvider; import trip.spi.tests.concurrency.PrinterRunner; public class ConcurrencyTest { final static int NUMBER_OF_CONSUMER = 30; - final ServiceProvider provider = new ServiceProvider(); + final DefaultServiceProvider provider = new DefaultServiceProvider(); final ExecutorService executor = Executors.newCachedThreadPool(); final CountDownLatch counter = new CountDownLatch( NUMBER_OF_CONSUMER * 2 ); diff --git a/trip-integration-tests/tests/trip/spi/tests/GeneratedCodeAndMetaINFTest.java b/trip-integration-tests/tests/trip/spi/tests/GeneratedCodeAndMetaINFTest.java index a66a299..1b113dd 100644 --- a/trip-integration-tests/tests/trip/spi/tests/GeneratedCodeAndMetaINFTest.java +++ b/trip-integration-tests/tests/trip/spi/tests/GeneratedCodeAndMetaINFTest.java @@ -3,33 +3,33 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; +import java.lang.annotation.Annotation; +import java.util.Arrays; + import org.junit.Test; import trip.spi.Producer; import trip.spi.ProviderContext; -import trip.spi.ServiceProvider; +import trip.spi.DefaultServiceProvider; import trip.spi.ServiceProviderException; +import trip.spi.helpers.filter.QualifierCondition; +import trip.spi.tests.ann.Ajax; +import trip.spi.tests.ann.DarkKnight; +import trip.spi.tests.ann.Foo; public class GeneratedCodeAndMetaINFTest { - final ServiceProvider provider = new ServiceProvider(); - - // @Test - // public void grantThatGenerateNewHelloWorld() throws - // ServiceProviderException { - // final HelloWorld helloWorld = this.provider.load( HelloWorld.class ); - // assertEquals( "Helllooooo", helloWorld.toString() ); - // } + final DefaultServiceProvider provider = new DefaultServiceProvider(); @Test public void grantThatGenerateNewHelloFoo() throws ServiceProviderException { - final HelloWorld helloWorld = this.provider.load( HelloWorld.class, "foo" ); + final HelloWorld helloWorld = this.provider.load( HelloWorld.class, qualifier(Foo.class) ); assertEquals( "Fooo!!!", helloWorld.toString() ); } @Test public void grantThatCouldRetrieveAjaxFromMars() throws ServiceProviderException { - final Hero hero = this.provider.load( Hero.class, "ajax" ); + final Hero hero = this.provider.load( Hero.class, qualifier(Ajax.class) ); assertNotNull( "No 'Hero' implementations found", hero ); assertEquals( "Expected 'Hero' should be 'AjaxFromMars' instance", hero.getClass(), AjaxFromMars.class ); @@ -39,13 +39,17 @@ public void grantThatCouldRetrieveAjaxFromMars() throws ServiceProviderException @Test public void grantThatCouldRetrieveBatman() throws ServiceProviderException { - final Hero hero = this.provider.load( Hero.class, "batman" ); + final Hero hero = this.provider.load( Hero.class, qualifier(DarkKnight.class) ); assertNotNull( hero ); assertEquals( Batman.class, hero.getClass() ); final Batman batman = (Batman)hero; assertEquals( "'batman' doesn't provide the expected string", "Mars", batman.getWorld() ); } + QualifierCondition qualifier( Class ann ){ + return new QualifierCondition<>(Arrays.asList(ann)); + } + @Producer public String produceAGenericString( final ProviderContext context ) { return null; diff --git a/trip-integration-tests/tests/trip/spi/tests/InjectionOfServiceDefinedByItsExposedTypeTest.java b/trip-integration-tests/tests/trip/spi/tests/InjectionOfServiceDefinedByItsExposedTypeTest.java index 2c0584f..4dba4c6 100644 --- a/trip-integration-tests/tests/trip/spi/tests/InjectionOfServiceDefinedByItsExposedTypeTest.java +++ b/trip-integration-tests/tests/trip/spi/tests/InjectionOfServiceDefinedByItsExposedTypeTest.java @@ -7,12 +7,12 @@ import org.junit.Test; import trip.spi.Provided; -import trip.spi.ServiceProvider; +import trip.spi.DefaultServiceProvider; import trip.spi.ServiceProviderException; public class InjectionOfServiceDefinedByItsExposedTypeTest { - final ServiceProvider provider = new ServiceProvider(); + final DefaultServiceProvider provider = new DefaultServiceProvider(); @Provided( exposedAs = Bean.class ) SerializableBean bean; diff --git a/trip-integration-tests/tests/trip/spi/tests/PostConstructAndPreDestroyStatelessServiceTest.java b/trip-integration-tests/tests/trip/spi/tests/PostConstructAndPreDestroyStatelessServiceTest.java index 6904a0c..88cdc3c 100644 --- a/trip-integration-tests/tests/trip/spi/tests/PostConstructAndPreDestroyStatelessServiceTest.java +++ b/trip-integration-tests/tests/trip/spi/tests/PostConstructAndPreDestroyStatelessServiceTest.java @@ -7,7 +7,7 @@ import org.junit.Test; import trip.spi.Provided; -import trip.spi.ServiceProvider; +import trip.spi.DefaultServiceProvider; import trip.spi.ServiceProviderException; public class PostConstructAndPreDestroyStatelessServiceTest { @@ -17,7 +17,7 @@ public class PostConstructAndPreDestroyStatelessServiceTest { @Before public void provideDependencies() throws ServiceProviderException { - new ServiceProvider().provideOn( this ); + new DefaultServiceProvider().provideOn( this ); } @Test diff --git a/trip-integration-tests/tests/trip/spi/tests/SingletonsAndStatelessProducerTest.java b/trip-integration-tests/tests/trip/spi/tests/SingletonsAndStatelessProducerTest.java index 86bd9b8..e09c51a 100644 --- a/trip-integration-tests/tests/trip/spi/tests/SingletonsAndStatelessProducerTest.java +++ b/trip-integration-tests/tests/trip/spi/tests/SingletonsAndStatelessProducerTest.java @@ -6,12 +6,12 @@ import org.junit.Test; -import trip.spi.ServiceProvider; +import trip.spi.DefaultServiceProvider; import trip.spi.ServiceProviderException; public class SingletonsAndStatelessProducerTest { - final ServiceProvider provider = new ServiceProvider(); + final DefaultServiceProvider provider = new DefaultServiceProvider(); @Test public void ensureThatProduceThreeDifferentNumbers() throws ServiceProviderException { @@ -26,15 +26,15 @@ public void ensureThatCantProduceThreeDifferentShorts() throws ServiceProviderEx assertThat( provider.load( Short.class ), is( (short)0 ) ); assertThat( provider.load( Short.class ), is( (short)0 ) ); } - + @Test public void ensureThatCanProduceUnrepeatedShortsWhenManuallyCreated(){ - ProducerOfShorts producerOfShorts = new StatelessProvidedProducerOfShorts(); + final ProducerOfShorts producerOfShorts = new StatelessProvidedProducerOfShorts(); assertThat( producerOfShorts.produceShort(), is( (short)0 ) ); assertThat( producerOfShorts.produceShort(), is( (short)1 ) ); assertThat( producerOfShorts.produceShort(), is( (short)2 ) ); } - + @Test public void ensureThatCantProduceUnrepeatedShortsWhenCreatedByServiceProvider() throws ServiceProviderException{ ProducerOfShorts producerOfShorts = provider.load( ProducerOfShorts.class ); @@ -47,7 +47,9 @@ public void ensureThatCantProduceUnrepeatedShortsWhenCreatedByServiceProvider() @Test public void ensureThatCanListStatelessClassForProducerOfShort() { - Class implementation = provider.loadClassImplementing( ProducerOfShorts.class ); - assertNotNull( implementation ); + final Iterable> implementations = provider.loadClassesImplementing( ProducerOfShorts.class ); + assertNotNull( implementations ); + for ( final Class cls : implementations ) + assertNotNull( cls ); } } diff --git a/trip-integration-tests/tests/trip/spi/tests/StatelessAndSingletonServicesAtSameInterface.java b/trip-integration-tests/tests/trip/spi/tests/StatelessAndSingletonServicesAtSameInterface.java index 49f0b68..8f32ecc 100644 --- a/trip-integration-tests/tests/trip/spi/tests/StatelessAndSingletonServicesAtSameInterface.java +++ b/trip-integration-tests/tests/trip/spi/tests/StatelessAndSingletonServicesAtSameInterface.java @@ -9,12 +9,12 @@ import org.junit.Test; -import trip.spi.ServiceProvider; +import trip.spi.DefaultServiceProvider; import trip.spi.ServiceProviderException; public class StatelessAndSingletonServicesAtSameInterface { - final ServiceProvider provider = new ServiceProvider(); + final DefaultServiceProvider provider = new DefaultServiceProvider(); @Test public void ensureThatCouldFoundBothImplementations() throws ServiceProviderException { diff --git a/trip-integration-tests/tests/trip/spi/tests/singleton/Closeable.java b/trip-integration-tests/tests/trip/spi/tests/singleton/Closeable.java new file mode 100644 index 0000000..0ce947f --- /dev/null +++ b/trip-integration-tests/tests/trip/spi/tests/singleton/Closeable.java @@ -0,0 +1,6 @@ +package trip.spi.tests.singleton; + +public interface Closeable { + + void close(); +} diff --git a/trip-integration-tests/tests/trip/spi/tests/singleton/HelloWorldReader.java b/trip-integration-tests/tests/trip/spi/tests/singleton/HelloWorldReader.java new file mode 100644 index 0000000..2456a42 --- /dev/null +++ b/trip-integration-tests/tests/trip/spi/tests/singleton/HelloWorldReader.java @@ -0,0 +1,17 @@ +package trip.spi.tests.singleton; + +import trip.spi.Singleton; + +@Singleton +public class HelloWorldReader implements Closeable, Reader { + + @Override + public String read() { + return toString(); + } + + @Override + public void close() { + throw new UnsupportedOperationException(); + } +} diff --git a/trip-integration-tests/tests/trip/spi/tests/singleton/Reader.java b/trip-integration-tests/tests/trip/spi/tests/singleton/Reader.java new file mode 100644 index 0000000..a9aa6b8 --- /dev/null +++ b/trip-integration-tests/tests/trip/spi/tests/singleton/Reader.java @@ -0,0 +1,6 @@ +package trip.spi.tests.singleton; + +public interface Reader { + + String read(); +} diff --git a/trip-integration-tests/tests/trip/spi/tests/singleton/SingletonBehaviorTest.java b/trip-integration-tests/tests/trip/spi/tests/singleton/SingletonBehaviorTest.java new file mode 100644 index 0000000..cf17a2f --- /dev/null +++ b/trip-integration-tests/tests/trip/spi/tests/singleton/SingletonBehaviorTest.java @@ -0,0 +1,32 @@ +package trip.spi.tests.singleton; + +import static org.junit.Assert.assertSame; + +import org.junit.Before; +import org.junit.Test; + +import trip.spi.DefaultServiceProvider; +import trip.spi.Provided; + +public class SingletonBehaviorTest { + + @Provided + Closeable closeable; + + @Provided + Reader reader; + + @Provided + HelloWorldReader helloReader; + + @Before + public void provideDependencies(){ + new DefaultServiceProvider().provideOn(this); + } + + @Test + public void ensureThatBothInjectedDataIsSame(){ + assertSame(closeable, helloReader); + assertSame(reader, helloReader); + } +} diff --git a/trip-jsr-330-integration-tests/.gitignore b/trip-jsr-330-integration-tests/.gitignore new file mode 100644 index 0000000..b2ec7f2 --- /dev/null +++ b/trip-jsr-330-integration-tests/.gitignore @@ -0,0 +1 @@ +/.apt_generated diff --git a/trip-jsr-330-integration-tests/pom.xml b/trip-jsr-330-integration-tests/pom.xml new file mode 100644 index 0000000..4d134e9 --- /dev/null +++ b/trip-jsr-330-integration-tests/pom.xml @@ -0,0 +1,30 @@ + + 4.0.0 + + + io.skullabs.trip + 2.0.0-SNAPSHOT + trip-parent + + + trip-jsr-330-integration-tests + tRip: JSR 330 Integration Tests + jar + + + + ${project.groupId} + trip-core + + + ${project.groupId} + trip-processor + + + ${project.groupId} + trip-jsr-330 + + + + diff --git a/trip-jsr-330-integration-tests/source/trip/jsr/cdi/ann/Ajax.java b/trip-jsr-330-integration-tests/source/trip/jsr/cdi/ann/Ajax.java new file mode 100644 index 0000000..8d48fda --- /dev/null +++ b/trip-jsr-330-integration-tests/source/trip/jsr/cdi/ann/Ajax.java @@ -0,0 +1,12 @@ +package trip.jsr.cdi.ann; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +import javax.inject.Qualifier; + + +@Qualifier +@Retention(RetentionPolicy.RUNTIME) +public @interface Ajax { +} diff --git a/trip-jsr-330-integration-tests/source/trip/jsr/cdi/ann/DarkKnight.java b/trip-jsr-330-integration-tests/source/trip/jsr/cdi/ann/DarkKnight.java new file mode 100644 index 0000000..4ec15ec --- /dev/null +++ b/trip-jsr-330-integration-tests/source/trip/jsr/cdi/ann/DarkKnight.java @@ -0,0 +1,11 @@ +package trip.jsr.cdi.ann; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +import trip.spi.Qualifier; + +@Qualifier +@Retention(RetentionPolicy.RUNTIME) +public @interface DarkKnight { +} diff --git a/trip-jsr-330-integration-tests/source/trip/jsr/cdi/ann/Foo.java b/trip-jsr-330-integration-tests/source/trip/jsr/cdi/ann/Foo.java new file mode 100644 index 0000000..b4a3000 --- /dev/null +++ b/trip-jsr-330-integration-tests/source/trip/jsr/cdi/ann/Foo.java @@ -0,0 +1,11 @@ +package trip.jsr.cdi.ann; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +import trip.spi.Qualifier; + +@Qualifier +@Retention(RetentionPolicy.RUNTIME) +public @interface Foo { +} diff --git a/trip-jsr-330-integration-tests/source/trip/jsr/cdi/ann/Names.java b/trip-jsr-330-integration-tests/source/trip/jsr/cdi/ann/Names.java new file mode 100644 index 0000000..537b811 --- /dev/null +++ b/trip-jsr-330-integration-tests/source/trip/jsr/cdi/ann/Names.java @@ -0,0 +1,11 @@ +package trip.jsr.cdi.ann; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +import trip.spi.Qualifier; + +@Qualifier +@Retention(RetentionPolicy.RUNTIME) +public @interface Names { +} diff --git a/trip-jsr-330/pom.xml b/trip-jsr-330/pom.xml new file mode 100644 index 0000000..43b46d2 --- /dev/null +++ b/trip-jsr-330/pom.xml @@ -0,0 +1,44 @@ + + 4.0.0 + + + io.skullabs.trip + 2.0.0-SNAPSHOT + trip-parent + + + trip-jsr-330 + tRip: JSR 330 support + jar + + + + ${project.groupId} + trip-core + + + ${project.groupId} + trip-processor + provided + + + javax.inject + javax.inject + 1 + + + + + + + true + org.apache.maven.plugins + maven-compiler-plugin + + none + + + + + diff --git a/trip-jsr-330/source/META-INF/services/javax.annotation.processing.Processor b/trip-jsr-330/source/META-INF/services/javax.annotation.processing.Processor new file mode 100644 index 0000000..36b0f97 --- /dev/null +++ b/trip-jsr-330/source/META-INF/services/javax.annotation.processing.Processor @@ -0,0 +1 @@ +trip.jsr.cdi.OptionalCDIProcessor \ No newline at end of file diff --git a/trip-jsr-330/source/trip/jsr/cdi/CDILoaderOfClasses.java b/trip-jsr-330/source/trip/jsr/cdi/CDILoaderOfClasses.java new file mode 100644 index 0000000..daf49d5 --- /dev/null +++ b/trip-jsr-330/source/trip/jsr/cdi/CDILoaderOfClasses.java @@ -0,0 +1,25 @@ +package trip.jsr.cdi; + +@SuppressWarnings("unchecked") +abstract class CDILoaderOfClasses { + + static Class loadClass( String className ){ + try { + return (Class)Class.forName(className); + } catch (final ClassNotFoundException e) { + return null; + } + } + + static boolean isClassPresent( String className ){ + return loadClass(className) != null; + } + + static T newInstanceOf( String className, Class supertype ){ + try { + return (T)loadClass(className).newInstance(); + } catch (InstantiationException | IllegalAccessException e) { + return null; + } + } +} diff --git a/trip-jsr-330/source/trip/jsr/cdi/CDIProcessor.java b/trip-jsr-330/source/trip/jsr/cdi/CDIProcessor.java new file mode 100644 index 0000000..fb2bd60 --- /dev/null +++ b/trip-jsr-330/source/trip/jsr/cdi/CDIProcessor.java @@ -0,0 +1,21 @@ +package trip.jsr.cdi; + +import static trip.jsr.cdi.CDILoaderOfClasses.loadClass; + +import java.io.IOException; + +import javax.annotation.processing.RoundEnvironment; +import javax.inject.Named; +import javax.inject.Singleton; + +import trip.spi.inject.ProvidedClassesProcessor; + +public class CDIProcessor extends ProvidedClassesProcessor { + + @Override + protected void process( RoundEnvironment roundEnv ) throws IOException { + processSingletons( roundEnv, Singleton.class ); + processStateless( roundEnv, Named.class ); + processProducers( roundEnv, loadClass("javax.enterprise.inject.Produces") ); + } +} diff --git a/trip-jsr-330/source/trip/jsr/cdi/CDIServiceProvider.java b/trip-jsr-330/source/trip/jsr/cdi/CDIServiceProvider.java new file mode 100644 index 0000000..5dffcf8 --- /dev/null +++ b/trip-jsr-330/source/trip/jsr/cdi/CDIServiceProvider.java @@ -0,0 +1,30 @@ +package trip.jsr.cdi; + +import javax.inject.Provider; + +import lombok.RequiredArgsConstructor; +import lombok.experimental.Delegate; +import trip.spi.DefaultServiceProvider; +import trip.spi.ProducerFactory; +import trip.spi.ServiceProvider; +import trip.spi.helpers.filter.AnyObject; +import trip.spi.helpers.filter.Condition; + +@RequiredArgsConstructor +@SuppressWarnings({"rawtypes","unchecked"}) +public class CDIServiceProvider implements ServiceProvider { + + private final static Condition ANY = new AnyObject<>(); + + @Delegate( types=ServiceProvider.class ) + final DefaultServiceProvider wrapped; + + public CDIServiceProvider() { + wrapped = new DefaultServiceProvider(); + } + + public Provider getProviderFor( Class clazz ) { + final ProducerFactory factory = wrapped.getProviderFor(clazz, ANY); + return new ProviderWrapper<>( factory ); + } +} diff --git a/trip-jsr-330/source/trip/jsr/cdi/OptionalCDIProcessor.java b/trip-jsr-330/source/trip/jsr/cdi/OptionalCDIProcessor.java new file mode 100644 index 0000000..ccf340f --- /dev/null +++ b/trip-jsr-330/source/trip/jsr/cdi/OptionalCDIProcessor.java @@ -0,0 +1,46 @@ +package trip.jsr.cdi; + +import static trip.jsr.cdi.CDILoaderOfClasses.isClassPresent; +import static trip.jsr.cdi.CDILoaderOfClasses.newInstanceOf; + +import java.util.Set; + +import javax.annotation.processing.AbstractProcessor; +import javax.annotation.processing.ProcessingEnvironment; +import javax.annotation.processing.RoundEnvironment; +import javax.annotation.processing.SupportedAnnotationTypes; +import javax.lang.model.SourceVersion; +import javax.lang.model.element.TypeElement; + +@SupportedAnnotationTypes( "javax.inject.*" ) +public class OptionalCDIProcessor extends AbstractProcessor { + + private static final String DEFAULT_PROCESSOR = "trip.spi.inject.ProvidedClassesProcessor"; + + AbstractProcessor cdiProcessor; + + @Override + public synchronized void init(ProcessingEnvironment processingEnv) { + super.init(processingEnv); + if ( isClassPresent(DEFAULT_PROCESSOR) ) + cdiProcessor = newInstanceOf("trip.jsr.cdi.CDIProcessor", AbstractProcessor.class); + } + + @Override + public boolean process(Set annotations, RoundEnvironment roundEnv) { + if ( cdiProcessor != null ) + return cdiProcessor.process(annotations, roundEnv); + return false; + } + + /** + * We just return the latest version of whatever JDK we run on. Stupid? + * Yeah, but it's either that or warnings on all versions but 1. Blame Joe. + * + * PS: this method was copied from Project Lombok. ;) + */ + @Override + public SourceVersion getSupportedSourceVersion() { + return SourceVersion.values()[SourceVersion.values().length - 1]; + } +} diff --git a/trip-jsr-330/source/trip/jsr/cdi/ProviderWrapper.java b/trip-jsr-330/source/trip/jsr/cdi/ProviderWrapper.java new file mode 100644 index 0000000..0395338 --- /dev/null +++ b/trip-jsr-330/source/trip/jsr/cdi/ProviderWrapper.java @@ -0,0 +1,18 @@ +package trip.jsr.cdi; + +import javax.inject.Provider; + +import lombok.RequiredArgsConstructor; +import trip.spi.ProducerFactory; +import trip.spi.helpers.EmptyProviderContext; + +@RequiredArgsConstructor +public class ProviderWrapper implements Provider { + + final ProducerFactory factory; + + @Override + public T get() { + return factory.provide(EmptyProviderContext.INSTANCE); + } +} \ No newline at end of file diff --git a/trip-jsr-330/tests/trip/jsr/cdi/AnnotationWithReflectionTest.java b/trip-jsr-330/tests/trip/jsr/cdi/AnnotationWithReflectionTest.java new file mode 100644 index 0000000..af67d99 --- /dev/null +++ b/trip-jsr-330/tests/trip/jsr/cdi/AnnotationWithReflectionTest.java @@ -0,0 +1,22 @@ +package trip.jsr.cdi; + +import static org.junit.Assert.assertEquals; + +import java.lang.annotation.Annotation; + +import javax.inject.Inject; + +import lombok.SneakyThrows; + +import org.junit.Test; + +public class AnnotationWithReflectionTest { + + @SuppressWarnings("unchecked") + @Test + @SneakyThrows + public void ensure(){ + final Class ann = (Class)Class.forName("javax.inject.Inject"); + assertEquals( Inject.class, ann ); + } +} diff --git a/trip-processor/source/META-INF/provided-class.mustache b/trip-processor/source/META-INF/provided-class.mustache index 7e9fc2e..98b75d2 100644 --- a/trip-processor/source/META-INF/provided-class.mustache +++ b/trip-processor/source/META-INF/provided-class.mustache @@ -1,11 +1,9 @@ package {{packageName}}; -{{#name}} -@trip.spi.Singleton( name="{{.}}", exposedAs=trip.spi.ProducerFactory.class ) -{{/name}} -{{^name}} +{{#annotations}} +@{{.}} +{{/annotations}} @trip.spi.Singleton( exposedAs=trip.spi.ProducerFactory.class ) -{{/name}} public class {{typeName}}AutoGeneratedProvider{{providerName}} implements trip.spi.ProducerFactory<{{type}}> { @trip.spi.Provided trip.spi.ServiceProvider provider; diff --git a/trip-processor/source/META-INF/stateless-class.mustache b/trip-processor/source/META-INF/stateless-class.mustache index 5d07087..ccf7672 100644 --- a/trip-processor/source/META-INF/stateless-class.mustache +++ b/trip-processor/source/META-INF/stateless-class.mustache @@ -1,11 +1,6 @@ package {{packageName}}; -{{#serviceIdentificationName}} -@trip.spi.Singleton( name="{{.}}", exposedAs={{{typeCanonicalName}}}.class ) -{{/serviceIdentificationName}} -{{^serviceIdentificationName}} @trip.spi.Singleton( exposedAs={{typeCanonicalName}}.class ) -{{/serviceIdentificationName}} @trip.spi.GeneratedFromStatelessService public class {{typeName}}Stateless{{identifaction}} {{#exposedByClass}} diff --git a/trip-processor/source/trip/spi/inject/ProducerImplementation.java b/trip-processor/source/trip/spi/inject/ProducerImplementation.java index 28f8bd7..6deb7a5 100644 --- a/trip-processor/source/trip/spi/inject/ProducerImplementation.java +++ b/trip-processor/source/trip/spi/inject/ProducerImplementation.java @@ -10,9 +10,7 @@ import javax.lang.model.element.VariableElement; import javax.lang.model.type.DeclaredType; -import trip.spi.Producer; import trip.spi.ProviderContext; -import trip.spi.Stateless; public class ProducerImplementation implements GenerableClass { @@ -22,34 +20,35 @@ public class ProducerImplementation implements GenerableClass { final String providerMethod; final String type; final String typeName; - final String name; final boolean expectsContext; final String serviceFor; final boolean stateless; + final List annotations; public ProducerImplementation( final String packageName, final String provider, final String providedMethod, final String type, - final String typeName, final String name, + final String typeName, final boolean expectsContext, - final String serviceFor, final boolean stateless ) { + final String serviceFor, final boolean stateless, + final List annotations ) { this.packageName = stripGenericsFrom( packageName ); this.provider = stripGenericsFrom( provider ); this.providerMethod = stripGenericsFrom( providedMethod ); this.type = stripGenericsFrom( type ); this.typeName = stripGenericsFrom( typeName ); - this.name = name; this.expectsContext = expectsContext; this.serviceFor = serviceFor; this.providerName = String.valueOf( createIdentifier() ); this.stateless = stateless; + this.annotations = annotations; } private long createIdentifier() { final int hashCode = - String.format( "%s%s%s%s%s%s%s%s", + String.format( "%s%s%s%s%s%s%s", packageName, provider, providerMethod, - type, typeName, name, expectsContext, stateless ) + type, typeName, expectsContext, stateless ) .hashCode(); return hashCode & 0xffffffffl; @@ -68,9 +67,9 @@ public static ProducerImplementation from( final ExecutableElement element ) { provider, method.getSimpleName().toString(), typeAsString, typeName, - extractNameFrom( method ), measureIfExpectsContextAsParameter( method ), - SingletonImplementation.getProvidedServiceClassAsString( type ), false ); + SingletonImplementation.getProvidedServiceClassAsStringOrNull( type ), false, + SingletonImplementation.getQualifierAnnotation(method) ); } static boolean measureIfExpectsContextAsParameter( final ExecutableElement method ) { @@ -84,20 +83,6 @@ static boolean measureIfExpectsContextAsParameter( final ExecutableElement metho return true; } - static String extractNameFrom( final ExecutableElement element ) { - final Producer producer = element.getAnnotation( Producer.class ); - if ( !producer.name().isEmpty() ) - return producer.name(); - return null; - } - - static String extractNameFrom( final TypeElement element ) { - final Stateless stateless = element.getAnnotation( Stateless.class ); - if ( !stateless.name().isEmpty() ) - return stateless.name(); - return null; - } - static ExecutableElement assertElementIsMethod( final Element element ) { return (ExecutableElement)element; } @@ -126,10 +111,6 @@ public String typeName() { return this.typeName; } - public String name() { - return this.name; - } - @Override public String getGeneratedClassCanonicalName() { return String.format( "%s.%sAutoGeneratedProvider%s", diff --git a/trip-processor/source/trip/spi/inject/ProvidedClassesProcessor.java b/trip-processor/source/trip/spi/inject/ProvidedClassesProcessor.java index 7b46c5e..cd0a455 100644 --- a/trip-processor/source/trip/spi/inject/ProvidedClassesProcessor.java +++ b/trip-processor/source/trip/spi/inject/ProvidedClassesProcessor.java @@ -4,6 +4,7 @@ import java.io.FileWriter; import java.io.IOException; import java.io.Writer; +import java.lang.annotation.Annotation; import java.net.URI; import java.util.HashMap; import java.util.HashSet; @@ -20,6 +21,7 @@ import javax.lang.model.element.ElementKind; import javax.lang.model.element.ExecutableElement; import javax.lang.model.element.TypeElement; +import javax.lang.model.type.TypeMirror; import javax.tools.Diagnostic.Kind; import javax.tools.FileObject; import javax.tools.JavaFileObject; @@ -61,17 +63,19 @@ public boolean process( final Set annotations, final Roun return false; } - void process( final RoundEnvironment roundEnv ) throws IOException { - processSingletons( roundEnv ); - processStateless( roundEnv ); - processProducers( roundEnv ); + protected void process( final RoundEnvironment roundEnv ) throws IOException { + processSingletons( roundEnv, Singleton.class ); + processStateless( roundEnv, Stateless.class ); + processProducers( roundEnv, Producer.class ); } - void processStateless( final RoundEnvironment roundEnv ) throws IOException { - final Set annotatedElements = roundEnv.getElementsAnnotatedWith( Stateless.class ); - for ( final Element element : annotatedElements ) - if ( element.getKind() == ElementKind.CLASS ) - memorizeAServiceImplementation( StatelessClass.from( (TypeElement)element ) ); + public void processStateless( final RoundEnvironment roundEnv, Class ann ) throws IOException { + if ( ann != null ){ + final Set annotatedElements = roundEnv.getElementsAnnotatedWith( ann ); + for ( final Element element : annotatedElements ) + if ( element.getKind() == ElementKind.CLASS ) + memorizeAServiceImplementation( StatelessClass.from( (TypeElement)element ) ); + } } void memorizeAServiceImplementation( final StatelessClass clazz ) throws IOException { @@ -92,11 +96,29 @@ void createAStatelessClassFrom( final StatelessClass clazz ) throws IOException } } - void processSingletons( final RoundEnvironment roundEnv ) { - final Set annotatedElements = roundEnv.getElementsAnnotatedWith( Singleton.class ); - for ( final Element element : annotatedElements ) - if ( element.getKind() == ElementKind.CLASS ) - memorizeAServiceImplementation( SingletonImplementation.from( element ) ); + public void processSingletons( final RoundEnvironment roundEnv, Class ann ) throws IOException { + if ( ann != null ){ + final Set annotatedElements = roundEnv.getElementsAnnotatedWith( ann ); + for ( final Element element : annotatedElements ) + if ( element.getKind() == ElementKind.CLASS ) + memorizeAllImplementations((TypeElement)element); + } + } + + private void memorizeAllImplementations(final TypeElement type) throws IOException { + final String implementationClass = type.asType().toString(); + final TypeMirror superinterfaceOrClass = SingletonImplementation.getProvidedServiceClass(type); + if ( superinterfaceOrClass != null ){ + System.out.println( "MEM: " + implementationClass + " => " + superinterfaceOrClass.toString() ); + memorizeAServiceImplementation( new SingletonImplementation( superinterfaceOrClass.toString(), implementationClass ) ); + }else { + System.out.println( "MEM ALL INTERFACES FOR: " + implementationClass ); + for ( final TypeMirror interfaceType : type.getInterfaces() ){ + System.out.println( " :: INTERFACES: " + interfaceType.toString() ); + memorizeAServiceImplementation( new SingletonImplementation(interfaceType.toString(), implementationClass) ); + } + memorizeAServiceImplementation( new SingletonImplementation(implementationClass, implementationClass) ); + } } void memorizeAServiceImplementation( final SingletonImplementation from ) { @@ -121,11 +143,13 @@ private HashSet readAListWithAllCreatedClassesImplementing( final String return foundSingletons; } - void processProducers( final RoundEnvironment roundEnv ) throws IOException { - final Set annotatedElements = roundEnv.getElementsAnnotatedWith( Producer.class ); - for ( final Element element : annotatedElements ) - if ( element.getKind() == ElementKind.METHOD ) - createAProducerFrom( ProducerImplementation.from( (ExecutableElement)element ) ); + public void processProducers( final RoundEnvironment roundEnv, Class ann ) throws IOException { + if ( ann != null ){ + final Set annotatedElements = roundEnv.getElementsAnnotatedWith( ann ); + for ( final Element element : annotatedElements ) + if ( element.getKind() == ElementKind.METHOD ) + createAProducerFrom( ProducerImplementation.from( (ExecutableElement)element ) ); + } } void createAProducerFrom( final GenerableClass clazz ) throws IOException { @@ -158,7 +182,9 @@ String createClassCanonicalName( final ProducerImplementation clazz ) { void createSingletonMetaInf() throws IOException { for ( final String interfaceClass : this.singletons.keySet() ) { final Writer resource = createResource( SERVICES + interfaceClass ); + System.out.println( "Exposing implementations of " + interfaceClass ); for ( final String implementation : this.singletons.get( interfaceClass ) ) { + System.out.println( " > " + implementation ); log( "Exposing " + implementation + " as " + interfaceClass ); resource.write( implementation + EOL ); } @@ -195,11 +221,10 @@ Filer filer() { } private void log( final String msg ) { - System.out.println( msg ); - processingEnv.getMessager().printMessage( Kind.MANDATORY_WARNING, msg ); + processingEnv.getMessager().printMessage( Kind.NOTE, msg ); } - void flush() throws IOException { + public void flush() throws IOException { if ( !this.singletons.isEmpty() ) createSingletonMetaInf(); } diff --git a/trip-processor/source/trip/spi/inject/SingletonImplementation.java b/trip-processor/source/trip/spi/inject/SingletonImplementation.java index d40055a..d30030f 100644 --- a/trip-processor/source/trip/spi/inject/SingletonImplementation.java +++ b/trip-processor/source/trip/spi/inject/SingletonImplementation.java @@ -2,8 +2,15 @@ import static trip.spi.inject.NameTransformations.stripGenericsFrom; +import java.lang.annotation.Annotation; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import javax.lang.model.element.AnnotationMirror; import javax.lang.model.element.Element; import javax.lang.model.element.TypeElement; +import javax.lang.model.type.DeclaredType; import javax.lang.model.type.MirroredTypeException; import javax.lang.model.type.TypeMirror; @@ -12,42 +19,49 @@ public class SingletonImplementation { + public static final List> QUALIFIERS = + Arrays.asList( trip.spi.Qualifier.class ); + final String interfaceClass; final String implementationClass; - public SingletonImplementation( - final String interfaceClass, final String implementationClass ) { + public SingletonImplementation( final String interfaceClass, final String implementationClass ) { this.interfaceClass = stripGenericsFrom( interfaceClass ); this.implementationClass = stripGenericsFrom( implementationClass ); } - public static SingletonImplementation from( final Element element ) { - final TypeElement type = (TypeElement)element; - final String interfaceClass = getProvidedServiceClassAsString( type ); - return new SingletonImplementation( interfaceClass, type.asType().toString() ); + public String implementationClass() { + return this.implementationClass; + } + + public String interfaceClass() { + return this.interfaceClass; } public static String getProvidedServiceClassAsString( final TypeElement type ) { - TypeMirror typeMirror = getProvidedServiceClass( type ); - if ( typeMirror == null ) - return null; - return typeMirror.toString(); + return getProvidedServiceClassAsString(type, type.asType().toString()); } - public static TypeMirror getProvidedServiceClass( final TypeElement type ) { - if ( isAnnotatedForStateless( type ) ) - return getProvidedServiceClassForStateless( type ); - if ( isAnnotatedForSingleton( type ) ) - return getProvidedServiceClassForSingleton( type ); - return null; + public static String getProvidedServiceClassAsStringOrNull( final TypeElement type ) { + return getProvidedServiceClassAsString(type, null); } - public static String getProvidedServiceName( final TypeElement type ) { + private static String getProvidedServiceClassAsString(final TypeElement type, String defaultValue) { + final TypeMirror typeMirror = getProvidedServiceClass( type ); + if ( typeMirror != null ) + return typeMirror.toString(); + return defaultValue; + } + + public static TypeMirror getProvidedServiceClass( final TypeElement type ) { + TypeMirror foundType = null; if ( isAnnotatedForStateless( type ) ) - return type.getAnnotation( Stateless.class ).name(); - if ( isAnnotatedForSingleton( type ) ) - return type.getAnnotation( Singleton.class ).name(); - return null; + foundType = getProvidedServiceClassForStateless( type ); + if ( foundType == null && isAnnotatedForSingleton( type ) ) + foundType = getProvidedServiceClassForSingleton( type ); + if ( foundType == null && isAnnotatedForSingleton( type ) || isAnnotatedForStateless( type ) ) + foundType = type.asType(); + return foundType; } private static boolean isAnnotatedForStateless( final TypeElement type ) { @@ -61,7 +75,7 @@ private static boolean isAnnotatedForSingleton( final TypeElement type ) { private static TypeMirror getProvidedServiceClassForStateless( final TypeElement type ) { final TypeMirror statelessService = getProvidedStatelessAsTypeMirror( type ); if ( isStatelessAnnotationClassBlank( statelessService ) ) - return type.asType(); + return null; return statelessService; } @@ -83,7 +97,7 @@ private static boolean isStatelessAnnotationClassBlank( final TypeMirror provide private static TypeMirror getProvidedServiceClassForSingleton( final TypeElement type ) { final TypeMirror providedClass = getProvidedSingletonAsTypeMirror( type ); if ( isSingletonAnnotationBlank( providedClass ) ) - return type.asType(); + return null; return providedClass; } @@ -102,11 +116,22 @@ private static boolean isSingletonAnnotationBlank( final TypeMirror providedClas return providedClass.toString().equals( Singleton.class.getCanonicalName() ); } - public String implementationClass() { - return this.implementationClass; + public static List getQualifierAnnotation( final Element type ){ + final List qualifierAnn = new ArrayList<>(); + for (final AnnotationMirror annotationMirror : type.getAnnotationMirrors()) + for ( final Class annClass : QUALIFIERS ) + if ( isAnnotationPresent(annotationMirror.getAnnotationType().asElement(), annClass.getCanonicalName() ) ) + qualifierAnn.add(annotationMirror.getAnnotationType().toString()); + return qualifierAnn; } - public String interfaceClass() { - return this.interfaceClass; + static boolean isAnnotationPresent(final Element element, String canonicalName) { + for (final AnnotationMirror annotationMirror : element.getAnnotationMirrors()) { + final DeclaredType annotationType = annotationMirror.getAnnotationType(); + final TypeElement annotationElement = (TypeElement) annotationType.asElement(); + if ( annotationElement.getQualifiedName().contentEquals(canonicalName) ) + return true; + } + return false; } } diff --git a/trip-processor/source/trip/spi/inject/stateless/StatelessClass.java b/trip-processor/source/trip/spi/inject/stateless/StatelessClass.java index f1828a7..8e5914b 100644 --- a/trip-processor/source/trip/spi/inject/stateless/StatelessClass.java +++ b/trip-processor/source/trip/spi/inject/stateless/StatelessClass.java @@ -24,12 +24,6 @@ public class StatelessClass implements GenerableClass { */ final long identifaction; - /** - * An easy name to find the class when two or more services are provided to - * a same type. - */ - final String serviceIdentificationName; - /** * The package where the new class should be placed. */ @@ -71,7 +65,6 @@ public class StatelessClass implements GenerableClass { final List preDestroyMethods; /** - * @param serviceIdentificationName * @param typeCanonicalName * @param implementationCanonicalName * @param exposedByClass @@ -79,11 +72,10 @@ public class StatelessClass implements GenerableClass { * @param postConstructMethods * @param preDestroyMethods */ - public StatelessClass( final String serviceIdentificationName, final String typeCanonicalName, + public StatelessClass( final String typeCanonicalName, final String implementationCanonicalName, final boolean exposedByClass, final List exposedMethods, final List postConstructMethods, final List preDestroyMethods ) { - this.serviceIdentificationName = serviceIdentificationName; this.packageName = extractPackageNameFrom( implementationCanonicalName ); this.typeCanonicalName = typeCanonicalName; this.typeName = extractClassNameFrom( typeCanonicalName ); @@ -97,8 +89,8 @@ public StatelessClass( final String serviceIdentificationName, final String type private long createIdentifier() { final int hashCode = - String.format( "%s%s%s%s%s%s%s", - packageName, serviceIdentificationName, typeCanonicalName, + String.format( "%s%s%s%s%s%s", + packageName, typeCanonicalName, typeName, implementationCanonicalName, exposedByClass, exposedMethodsAsString() ) .hashCode(); @@ -127,10 +119,6 @@ public Long getIdentifaction() { return identifaction; } - public String getServiceIdentificationName() { - return serviceIdentificationName; - } - public String getPackageName() { return packageName; } @@ -164,12 +152,11 @@ public String getGeneratedClassCanonicalName() { } public static StatelessClass from( final TypeElement type ) { - final String serviceIdentificationName = SingletonImplementation.getProvidedServiceName( type ); final String typeCanonicalName = SingletonImplementation.getProvidedServiceClassAsString( type ); final String implementationCanonicalName = type.asType().toString(); final boolean exposedByClass = isImplementingClass( typeCanonicalName, type ); final List exposedMethods = retrieveExposedMethods( type ); - return new StatelessClass( serviceIdentificationName, typeCanonicalName, + return new StatelessClass( typeCanonicalName, implementationCanonicalName, exposedByClass, exposedMethods, retrieveMethodsAnnotatedWith( type, PostConstruct.class, javax.annotation.PostConstruct.class ), retrieveMethodsAnnotatedWith( type, PreDestroy.class, javax.annotation.PreDestroy.class ) ); @@ -193,6 +180,7 @@ static List retrieveExposedMethods( final TypeElement type ) { return list; } + @SafeVarargs static List retrieveMethodsAnnotatedWith( final TypeElement type, final Class... annotations ) { final List list = new ArrayList(); diff --git a/trip-processor/tests/stateless-class-exposing-class.txt b/trip-processor/tests/stateless-class-exposing-class.txt index b4ac935..2cef17e 100644 --- a/trip-processor/tests/stateless-class-exposing-class.txt +++ b/trip-processor/tests/stateless-class-exposing-class.txt @@ -1,8 +1,8 @@ package sample.project; -@trip.spi.Singleton( name="my-self", exposedAs=sample.project.ServiceFromInterface.class ) +@trip.spi.Singleton( exposedAs=sample.project.ServiceFromInterface.class ) @trip.spi.GeneratedFromStatelessService -public class ServiceFromInterfaceStateless3994859186 +public class ServiceFromInterfaceStateless3407109653 extends sample.project.ServiceFromInterface { @trip.spi.Provided trip.spi.ServiceProvider provider; @@ -10,11 +10,11 @@ public class ServiceFromInterfaceStateless3994859186 public void voidMethod( ) { try { final sample.project.ServiceFromInterface instance = newInstance(); - postConstructServiceFromInterface3994859186( instance ); + postConstructServiceFromInterface3407109653( instance ); try { instance.voidMethod( ); } finally { - preDestroyServiceFromInterface3994859186( instance ); + preDestroyServiceFromInterface3407109653( instance ); } } catch ( Throwable cause ) { throw new RuntimeException( cause ); @@ -24,12 +24,12 @@ public class ServiceFromInterfaceStateless3994859186 public Long sum( Double arg0,Integer arg1 ) { try { final sample.project.ServiceFromInterface instance = newInstance(); - postConstructServiceFromInterface3994859186( instance ); + postConstructServiceFromInterface3407109653( instance ); try { final Long returnValue = instance.sum( arg0,arg1 ); return returnValue; } finally { - preDestroyServiceFromInterface3994859186( instance ); + preDestroyServiceFromInterface3407109653( instance ); } } catch ( Throwable cause ) { throw new RuntimeException( cause ); @@ -42,12 +42,12 @@ public class ServiceFromInterfaceStateless3994859186 return instance; } - void postConstructServiceFromInterface3994859186( + void postConstructServiceFromInterface3407109653( final sample.project.ServiceFromInterface instance ) throws Throwable { instance.sum(); } - void preDestroyServiceFromInterface3994859186( + void preDestroyServiceFromInterface3407109653( final sample.project.ServiceFromInterface instance ) throws Throwable { instance.voidMethod(); } diff --git a/trip-processor/tests/trip/spi/inject/stateless/StatelessClassGeneratorTest.java b/trip-processor/tests/trip/spi/inject/stateless/StatelessClassGeneratorTest.java index 9bcbaf5..8aa677f 100644 --- a/trip-processor/tests/trip/spi/inject/stateless/StatelessClassGeneratorTest.java +++ b/trip-processor/tests/trip/spi/inject/stateless/StatelessClassGeneratorTest.java @@ -35,7 +35,7 @@ public void ensureThatGenerateTheExpectedClassFromExposedServiceByItSelf() throw StatelessClass createStatelessImplementationOfInterface() { return new StatelessClass( - "", "important.api.Interface", "sample.project.ServiceFromInterface", false, + "important.api.Interface", "sample.project.ServiceFromInterface", false, list( voidMethod(), returnableMethod() ), list( returnableMethod() ), list( voidMethod() ) ); @@ -43,7 +43,7 @@ StatelessClass createStatelessImplementationOfInterface() { StatelessClass createStatelessImplementationOfClass() { return new StatelessClass( - "my-self", "sample.project.ServiceFromInterface", + "sample.project.ServiceFromInterface", "sample.project.ServiceFromInterface", true, list( voidMethod(), returnableMethod() ), list( returnableMethod() ), @@ -58,6 +58,7 @@ ExposedMethod voidMethod() { return new ExposedMethod( "voidMethod", "void", emptyStringList() ); } + @SuppressWarnings("unchecked") List list( final T... ts ) { final List list = new ArrayList(); for ( final T t : ts ) From ed5f1bf8f7f48b473d6b4d1d84acb44e3b096a22 Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 15 Jul 2015 19:00:29 -0300 Subject: [PATCH 10/26] Regression fixed: stateless service support wasn't being exposed correctly --- .../source/trip/spi/helpers/SingleElementProvidableField.java | 1 + .../source/trip/spi/inject/SingletonImplementation.java | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/trip-core/source/trip/spi/helpers/SingleElementProvidableField.java b/trip-core/source/trip/spi/helpers/SingleElementProvidableField.java index b636648..c34bbc0 100644 --- a/trip-core/source/trip/spi/helpers/SingleElementProvidableField.java +++ b/trip-core/source/trip/spi/helpers/SingleElementProvidableField.java @@ -43,6 +43,7 @@ public static ProvidableField from( Collection> final Provided provided = field.getAnnotation( Provided.class ); final Class expectedClass = provided.exposedAs().equals( Provided.class ) ? field.getType() : provided.exposedAs(); + System.out.println( "SingleElementProvidableField: " + field + " => " + expectedClass ); return new SingleElementProvidableField( field, (Class)expectedClass, createInjectionCondition( qualifiers, field), diff --git a/trip-processor/source/trip/spi/inject/SingletonImplementation.java b/trip-processor/source/trip/spi/inject/SingletonImplementation.java index d30030f..5f8729f 100644 --- a/trip-processor/source/trip/spi/inject/SingletonImplementation.java +++ b/trip-processor/source/trip/spi/inject/SingletonImplementation.java @@ -59,7 +59,7 @@ public static TypeMirror getProvidedServiceClass( final TypeElement type ) { foundType = getProvidedServiceClassForStateless( type ); if ( foundType == null && isAnnotatedForSingleton( type ) ) foundType = getProvidedServiceClassForSingleton( type ); - if ( foundType == null && isAnnotatedForSingleton( type ) || isAnnotatedForStateless( type ) ) + if ( foundType == null && (isAnnotatedForSingleton( type ) || isAnnotatedForStateless( type ) ) ) foundType = type.asType(); return foundType; } From 6369c918ca1e4600f41b8e51a4b9e1eed00f64ee Mon Sep 17 00:00:00 2001 From: Miere Liniel Teixeira Date: Thu, 16 Jul 2015 01:10:23 -0300 Subject: [PATCH 11/26] Closes #33 --- .../trip.spi.helpers.QualifierExtractor | 1 - .../trip/spi/DefaultServiceProvider.java | 10 ++-- .../DefaultFieldQualifierExtractor.java | 15 ++++- .../spi/helpers/FieldQualifierExtractor.java | 4 ++ .../trip/spi/helpers/ProvidableClass.java | 6 +- .../trip/spi/helpers/QualifierExtractor.java | 14 +++++ .../helpers/SingleElementProvidableField.java | 3 +- .../trip/spi/helpers/filter/AnyObject.java | 7 +++ trip-jsr-330-integration-tests/pom.xml | 5 ++ .../trip/jsr/cdi/singleton/Closeable.java | 6 ++ .../jsr/cdi/singleton/HelloWorldReader.java | 17 ++++++ .../cdi/singleton/ListOfStringsProducer.java | 16 ++++++ .../tests/trip/jsr/cdi/singleton/Reader.java | 6 ++ .../cdi/singleton/SingletonBehaviorTest.java | 55 +++++++++++++++++++ trip-jsr-330/pom.xml | 24 +++++++- .../trip.spi.helpers.FieldQualifierExtractor | 1 + .../jsr/cdi/CDIFieldQualifierExtractor.java | 27 +++++++++ .../trip/jsr/cdi/CDILoaderOfClasses.java | 2 + .../source/trip/jsr/cdi/CDIProcessor.java | 8 +-- .../trip/jsr/cdi/CDIServiceProvider.java | 6 +- .../trip/jsr/cdi/OptionalCDIProcessor.java | 6 +- .../trip/jsr/cdi/SingleElementProvider.java | 16 ++++++ .../javax.annotation.processing.Processor | 2 +- .../{inject => processor}/GenerableClass.java | 2 +- .../NameTransformations.java | 2 +- .../ProducerImplementation.java | 4 +- .../SPIProcessor.java} | 18 +++--- .../SingletonImplementation.java | 11 ++-- .../stateless/ExposedMethod.java | 2 +- .../stateless/StatelessClass.java | 6 +- .../stateless/StatelessClassGenerator.java | 2 +- .../StatelessClassGeneratorTest.java | 4 ++ 32 files changed, 258 insertions(+), 50 deletions(-) delete mode 100644 trip-core/source/META-INF/services/trip.spi.helpers.QualifierExtractor create mode 100644 trip-jsr-330-integration-tests/tests/trip/jsr/cdi/singleton/Closeable.java create mode 100644 trip-jsr-330-integration-tests/tests/trip/jsr/cdi/singleton/HelloWorldReader.java create mode 100644 trip-jsr-330-integration-tests/tests/trip/jsr/cdi/singleton/ListOfStringsProducer.java create mode 100644 trip-jsr-330-integration-tests/tests/trip/jsr/cdi/singleton/Reader.java create mode 100644 trip-jsr-330-integration-tests/tests/trip/jsr/cdi/singleton/SingletonBehaviorTest.java create mode 100644 trip-jsr-330/source/META-INF/services/trip.spi.helpers.FieldQualifierExtractor create mode 100644 trip-jsr-330/source/trip/jsr/cdi/CDIFieldQualifierExtractor.java create mode 100644 trip-jsr-330/source/trip/jsr/cdi/SingleElementProvider.java rename trip-processor/source/trip/spi/{inject => processor}/GenerableClass.java (74%) rename trip-processor/source/trip/spi/{inject => processor}/NameTransformations.java (83%) rename trip-processor/source/trip/spi/{inject => processor}/ProducerImplementation.java (97%) rename trip-processor/source/trip/spi/{inject/ProvidedClassesProcessor.java => processor/SPIProcessor.java} (94%) rename trip-processor/source/trip/spi/{inject => processor}/SingletonImplementation.java (93%) rename trip-processor/source/trip/spi/{inject => processor}/stateless/ExposedMethod.java (98%) rename trip-processor/source/trip/spi/{inject => processor}/stateless/StatelessClass.java (97%) rename trip-processor/source/trip/spi/{inject => processor}/stateless/StatelessClassGenerator.java (92%) diff --git a/trip-core/source/META-INF/services/trip.spi.helpers.QualifierExtractor b/trip-core/source/META-INF/services/trip.spi.helpers.QualifierExtractor deleted file mode 100644 index f4ada1b..0000000 --- a/trip-core/source/META-INF/services/trip.spi.helpers.QualifierExtractor +++ /dev/null @@ -1 +0,0 @@ -trip.spi.helpers.QualifierExtractor \ No newline at end of file diff --git a/trip-core/source/trip/spi/DefaultServiceProvider.java b/trip-core/source/trip/spi/DefaultServiceProvider.java index 539c09f..cbc5f09 100644 --- a/trip-core/source/trip/spi/DefaultServiceProvider.java +++ b/trip-core/source/trip/spi/DefaultServiceProvider.java @@ -17,6 +17,7 @@ import trip.spi.helpers.filter.AnyObject; import trip.spi.helpers.filter.Condition; +@SuppressWarnings( { "rawtypes", "unchecked" } ) public class DefaultServiceProvider implements ServiceProvider { final SingletonContext singletonContext = new SingletonContext(); @@ -64,17 +65,17 @@ protected ProducerFactoryMap loadAllProducers() { @Override public T load( final Class interfaceClazz ) { - return load( interfaceClazz, new AnyObject() ); + return load( interfaceClazz, AnyObject.instance() ); } @Override public T load( final Class interfaceClazz, final Condition condition ) { - return load( interfaceClazz, condition, new EmptyProviderContext() ); + return load( interfaceClazz, condition, EmptyProviderContext.INSTANCE ); } @Override public T load( final Class interfaceClazz, final ProviderContext context ) { - return load( interfaceClazz, new AnyObject(), context ); + return load( interfaceClazz, AnyObject.instance(), context ); } @Override @@ -92,7 +93,6 @@ public Iterable loadAll( final Class interfaceClazz, final Condition Iterable loadAll( final Class interfaceClazz ) { Iterable iterable = (Iterable)this.providers.get( interfaceClazz ); if ( iterable == null ) @@ -123,7 +123,6 @@ protected Iterable loadServiceProvidersFor( return singletonContext.instantiate(iterableInterfaces); } - @SuppressWarnings( { "rawtypes", "unchecked" } ) public Iterable> loadClassesImplementing( final Class interfaceClazz ) { Iterable> implementations = (Iterable)implementedClasses.get( interfaceClazz ); if ( implementations == null ) @@ -188,7 +187,6 @@ private T produceFromFactory( final Class interfaceClazz, final Condition return null; } - @SuppressWarnings( "unchecked" ) public ProducerFactory getProviderFor( final Class interfaceClazz, final Condition condition ) { if ( this.producers == null ) return null; diff --git a/trip-core/source/trip/spi/helpers/DefaultFieldQualifierExtractor.java b/trip-core/source/trip/spi/helpers/DefaultFieldQualifierExtractor.java index dce9faa..d5cf9c0 100644 --- a/trip-core/source/trip/spi/helpers/DefaultFieldQualifierExtractor.java +++ b/trip-core/source/trip/spi/helpers/DefaultFieldQualifierExtractor.java @@ -1,13 +1,26 @@ package trip.spi.helpers; import java.lang.annotation.Annotation; +import java.lang.reflect.Field; +import trip.spi.Provided; +import trip.spi.ProvidedServices; import trip.spi.Qualifier; public class DefaultFieldQualifierExtractor implements FieldQualifierExtractor { @Override public boolean isAnnotatedWithQualifierAnnotation(Class ann) { - return ann.isAnnotationPresent(Qualifier.class); + return ann.isAnnotationPresent( Qualifier.class ); + } + + @Override + public boolean isASingleElementProvider( Field field ) { + return field.isAnnotationPresent( Provided.class ); + } + + @Override + public boolean isAManyElementsProvider( Field field ) { + return field.isAnnotationPresent( ProvidedServices.class ); } } diff --git a/trip-core/source/trip/spi/helpers/FieldQualifierExtractor.java b/trip-core/source/trip/spi/helpers/FieldQualifierExtractor.java index bfd92cf..99a1835 100644 --- a/trip-core/source/trip/spi/helpers/FieldQualifierExtractor.java +++ b/trip-core/source/trip/spi/helpers/FieldQualifierExtractor.java @@ -16,4 +16,8 @@ default List> extractQualifiersFrom(Field field) { } boolean isAnnotatedWithQualifierAnnotation(Class ann); + + boolean isASingleElementProvider( Field field ); + + boolean isAManyElementsProvider( Field field ); } diff --git a/trip-core/source/trip/spi/helpers/ProvidableClass.java b/trip-core/source/trip/spi/helpers/ProvidableClass.java index 488f512..ad32237 100644 --- a/trip-core/source/trip/spi/helpers/ProvidableClass.java +++ b/trip-core/source/trip/spi/helpers/ProvidableClass.java @@ -9,8 +9,6 @@ import lombok.RequiredArgsConstructor; import trip.spi.GeneratedFromStatelessService; -import trip.spi.Provided; -import trip.spi.ProvidedServices; import trip.spi.ServiceProvider; import trip.spi.ServiceProviderException; @@ -45,9 +43,9 @@ static Iterable readClassProvidableFields( QualifierExtractor e static void populateWithProvidableFields( QualifierExtractor extractor, Class targetClazz, List providableFields ) { for ( final Field field : targetClazz.getDeclaredFields() ){ final Collection> qualifiers = extractQualifiersFromAvoidingNPEWhenCreatingQualifierExtractor(extractor, field); - if ( field.isAnnotationPresent( Provided.class ) ) + if ( extractor.isASingleElementProvider( field ) ) providableFields.add( SingleElementProvidableField.from( qualifiers, field ) ); - else if ( field.isAnnotationPresent( ProvidedServices.class ) ) + else if ( extractor.isAManyElementsProvider( field ) ) providableFields.add( ManyElementsProvidableField.from( qualifiers, field ) ); } } diff --git a/trip-core/source/trip/spi/helpers/QualifierExtractor.java b/trip-core/source/trip/spi/helpers/QualifierExtractor.java index 68d3145..9f3196a 100644 --- a/trip-core/source/trip/spi/helpers/QualifierExtractor.java +++ b/trip-core/source/trip/spi/helpers/QualifierExtractor.java @@ -25,4 +25,18 @@ public boolean isAnnotatedWithQualifierAnnotation( Class a return true; return false; } + + public boolean isASingleElementProvider( Field field ) { + for ( final FieldQualifierExtractor extractor : extractors ) + if ( extractor.isASingleElementProvider( field ) ) + return true; + return false; + } + + public boolean isAManyElementsProvider( Field field ) { + for ( final FieldQualifierExtractor extractor : extractors ) + if ( extractor.isAManyElementsProvider( field ) ) + return true; + return false; + } } diff --git a/trip-core/source/trip/spi/helpers/SingleElementProvidableField.java b/trip-core/source/trip/spi/helpers/SingleElementProvidableField.java index c34bbc0..b828635 100644 --- a/trip-core/source/trip/spi/helpers/SingleElementProvidableField.java +++ b/trip-core/source/trip/spi/helpers/SingleElementProvidableField.java @@ -41,9 +41,8 @@ public void set( final Object instance, final Object value ) throws IllegalArgum public static ProvidableField from( Collection> qualifiers, final Field field ) { field.setAccessible( true ); final Provided provided = field.getAnnotation( Provided.class ); - final Class expectedClass = provided.exposedAs().equals( Provided.class ) + final Class expectedClass = provided == null || provided.exposedAs().equals( Provided.class ) ? field.getType() : provided.exposedAs(); - System.out.println( "SingleElementProvidableField: " + field + " => " + expectedClass ); return new SingleElementProvidableField( field, (Class)expectedClass, createInjectionCondition( qualifiers, field), diff --git a/trip-core/source/trip/spi/helpers/filter/AnyObject.java b/trip-core/source/trip/spi/helpers/filter/AnyObject.java index e588815..ad25e58 100644 --- a/trip-core/source/trip/spi/helpers/filter/AnyObject.java +++ b/trip-core/source/trip/spi/helpers/filter/AnyObject.java @@ -3,8 +3,15 @@ import lombok.NoArgsConstructor; @NoArgsConstructor +@SuppressWarnings( { "rawtypes", "unchecked" } ) public class AnyObject implements Condition { + private static final Condition INSTANCE = new AnyObject(); + + public static Condition instance() { + return INSTANCE; + } + @Override public boolean check(Object object) { return true; diff --git a/trip-jsr-330-integration-tests/pom.xml b/trip-jsr-330-integration-tests/pom.xml index 4d134e9..f905fde 100644 --- a/trip-jsr-330-integration-tests/pom.xml +++ b/trip-jsr-330-integration-tests/pom.xml @@ -25,6 +25,11 @@ ${project.groupId} trip-jsr-330 + + javax.enterprise + cdi-api + 1.2 + diff --git a/trip-jsr-330-integration-tests/tests/trip/jsr/cdi/singleton/Closeable.java b/trip-jsr-330-integration-tests/tests/trip/jsr/cdi/singleton/Closeable.java new file mode 100644 index 0000000..aee56e2 --- /dev/null +++ b/trip-jsr-330-integration-tests/tests/trip/jsr/cdi/singleton/Closeable.java @@ -0,0 +1,6 @@ +package trip.jsr.cdi.singleton; + +public interface Closeable { + + void close(); +} diff --git a/trip-jsr-330-integration-tests/tests/trip/jsr/cdi/singleton/HelloWorldReader.java b/trip-jsr-330-integration-tests/tests/trip/jsr/cdi/singleton/HelloWorldReader.java new file mode 100644 index 0000000..719f45e --- /dev/null +++ b/trip-jsr-330-integration-tests/tests/trip/jsr/cdi/singleton/HelloWorldReader.java @@ -0,0 +1,17 @@ +package trip.jsr.cdi.singleton; + +import javax.inject.Singleton; + +@Singleton +public class HelloWorldReader implements Closeable, Reader { + + @Override + public String read() { + return toString(); + } + + @Override + public void close() { + throw new UnsupportedOperationException(); + } +} diff --git a/trip-jsr-330-integration-tests/tests/trip/jsr/cdi/singleton/ListOfStringsProducer.java b/trip-jsr-330-integration-tests/tests/trip/jsr/cdi/singleton/ListOfStringsProducer.java new file mode 100644 index 0000000..0d2f1d2 --- /dev/null +++ b/trip-jsr-330-integration-tests/tests/trip/jsr/cdi/singleton/ListOfStringsProducer.java @@ -0,0 +1,16 @@ +package trip.jsr.cdi.singleton; + +import java.util.Arrays; +import java.util.List; + +import javax.enterprise.inject.Produces; +import javax.inject.Singleton; + +@Singleton +public class ListOfStringsProducer { + + @Produces + public List produceNames() { + return Arrays.asList( "Miere", "Poppins", "Helden", "Lissie" ); + } +} diff --git a/trip-jsr-330-integration-tests/tests/trip/jsr/cdi/singleton/Reader.java b/trip-jsr-330-integration-tests/tests/trip/jsr/cdi/singleton/Reader.java new file mode 100644 index 0000000..9ab02ce --- /dev/null +++ b/trip-jsr-330-integration-tests/tests/trip/jsr/cdi/singleton/Reader.java @@ -0,0 +1,6 @@ +package trip.jsr.cdi.singleton; + +public interface Reader { + + String read(); +} diff --git a/trip-jsr-330-integration-tests/tests/trip/jsr/cdi/singleton/SingletonBehaviorTest.java b/trip-jsr-330-integration-tests/tests/trip/jsr/cdi/singleton/SingletonBehaviorTest.java new file mode 100644 index 0000000..4622733 --- /dev/null +++ b/trip-jsr-330-integration-tests/tests/trip/jsr/cdi/singleton/SingletonBehaviorTest.java @@ -0,0 +1,55 @@ +package trip.jsr.cdi.singleton; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertSame; + +import java.util.List; + +import javax.inject.Inject; +import javax.inject.Provider; + +import org.junit.Before; +import org.junit.Test; + +import trip.jsr.cdi.CDIServiceProvider; + +public class SingletonBehaviorTest { + + final CDIServiceProvider provider = new CDIServiceProvider(); + + @Inject + Closeable closeable; + + @Inject + Reader reader; + + @Inject + HelloWorldReader helloReader; + + @Inject + List names; + + @Before + public void provideDependencies(){ + provider.provideOn( this ); + } + + @Test + public void ensureThatBothInjectedDataIsSame(){ + assertSame(closeable, helloReader); + assertSame(reader, helloReader); + } + + @Test + public void ensureThatHaveTheNames() { + assertNotNull( names ); + assertEquals( "Miere", names.get( 0 ) ); + } + + @Test + public void ensureThatGetProviderMethodWorksAsExpected() { + final Provider providerFor = provider.getProviderFor( HelloWorldReader.class ); + assertEquals( helloReader, providerFor.get() ); + } +} diff --git a/trip-jsr-330/pom.xml b/trip-jsr-330/pom.xml index 43b46d2..fd78a6f 100644 --- a/trip-jsr-330/pom.xml +++ b/trip-jsr-330/pom.xml @@ -35,9 +35,27 @@ true org.apache.maven.plugins maven-compiler-plugin - - none - + + + default-compile + compile + + compile + + + + lombok.core.AnnotationProcessor + + + + + default-testCompile + test-compile + + testCompile + + + diff --git a/trip-jsr-330/source/META-INF/services/trip.spi.helpers.FieldQualifierExtractor b/trip-jsr-330/source/META-INF/services/trip.spi.helpers.FieldQualifierExtractor new file mode 100644 index 0000000..f0012d6 --- /dev/null +++ b/trip-jsr-330/source/META-INF/services/trip.spi.helpers.FieldQualifierExtractor @@ -0,0 +1 @@ +trip.jsr.cdi.CDIFieldQualifierExtractor \ No newline at end of file diff --git a/trip-jsr-330/source/trip/jsr/cdi/CDIFieldQualifierExtractor.java b/trip-jsr-330/source/trip/jsr/cdi/CDIFieldQualifierExtractor.java new file mode 100644 index 0000000..8de1bfb --- /dev/null +++ b/trip-jsr-330/source/trip/jsr/cdi/CDIFieldQualifierExtractor.java @@ -0,0 +1,27 @@ +package trip.jsr.cdi; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Field; + +import javax.inject.Inject; +import javax.inject.Qualifier; + +import trip.spi.helpers.FieldQualifierExtractor; + +public class CDIFieldQualifierExtractor implements FieldQualifierExtractor { + + @Override + public boolean isAnnotatedWithQualifierAnnotation(Class ann) { + return ann.isAnnotationPresent( Qualifier.class ); + } + + @Override + public boolean isASingleElementProvider( Field field ) { + return field.isAnnotationPresent( Inject.class ); + } + + @Override + public boolean isAManyElementsProvider( Field field ) { + return false; + } +} diff --git a/trip-jsr-330/source/trip/jsr/cdi/CDILoaderOfClasses.java b/trip-jsr-330/source/trip/jsr/cdi/CDILoaderOfClasses.java index daf49d5..07ed321 100644 --- a/trip-jsr-330/source/trip/jsr/cdi/CDILoaderOfClasses.java +++ b/trip-jsr-330/source/trip/jsr/cdi/CDILoaderOfClasses.java @@ -3,6 +3,8 @@ @SuppressWarnings("unchecked") abstract class CDILoaderOfClasses { + static final String ANNOTATION_PRODUCES = "javax.enterprise.inject.Produces"; + static Class loadClass( String className ){ try { return (Class)Class.forName(className); diff --git a/trip-jsr-330/source/trip/jsr/cdi/CDIProcessor.java b/trip-jsr-330/source/trip/jsr/cdi/CDIProcessor.java index fb2bd60..203733a 100644 --- a/trip-jsr-330/source/trip/jsr/cdi/CDIProcessor.java +++ b/trip-jsr-330/source/trip/jsr/cdi/CDIProcessor.java @@ -1,6 +1,6 @@ package trip.jsr.cdi; -import static trip.jsr.cdi.CDILoaderOfClasses.loadClass; +import static trip.jsr.cdi.CDILoaderOfClasses.*; import java.io.IOException; @@ -8,14 +8,14 @@ import javax.inject.Named; import javax.inject.Singleton; -import trip.spi.inject.ProvidedClassesProcessor; +import trip.spi.processor.SPIProcessor; -public class CDIProcessor extends ProvidedClassesProcessor { +public class CDIProcessor extends SPIProcessor { @Override protected void process( RoundEnvironment roundEnv ) throws IOException { processSingletons( roundEnv, Singleton.class ); processStateless( roundEnv, Named.class ); - processProducers( roundEnv, loadClass("javax.enterprise.inject.Produces") ); + processProducers( roundEnv, loadClass( ANNOTATION_PRODUCES ) ); } } diff --git a/trip-jsr-330/source/trip/jsr/cdi/CDIServiceProvider.java b/trip-jsr-330/source/trip/jsr/cdi/CDIServiceProvider.java index 5dffcf8..b827b81 100644 --- a/trip-jsr-330/source/trip/jsr/cdi/CDIServiceProvider.java +++ b/trip-jsr-330/source/trip/jsr/cdi/CDIServiceProvider.java @@ -9,6 +9,7 @@ import trip.spi.ServiceProvider; import trip.spi.helpers.filter.AnyObject; import trip.spi.helpers.filter.Condition; +import trip.spi.helpers.filter.Filter; @RequiredArgsConstructor @SuppressWarnings({"rawtypes","unchecked"}) @@ -25,6 +26,9 @@ public CDIServiceProvider() { public Provider getProviderFor( Class clazz ) { final ProducerFactory factory = wrapped.getProviderFor(clazz, ANY); - return new ProviderWrapper<>( factory ); + if ( factory != null ) + return new ProviderWrapper<>( factory ); + T t = Filter.first( wrapped.loadAll( clazz ), AnyObject.instance() ); + return new SingleElementProvider( t ); } } diff --git a/trip-jsr-330/source/trip/jsr/cdi/OptionalCDIProcessor.java b/trip-jsr-330/source/trip/jsr/cdi/OptionalCDIProcessor.java index ccf340f..ec74746 100644 --- a/trip-jsr-330/source/trip/jsr/cdi/OptionalCDIProcessor.java +++ b/trip-jsr-330/source/trip/jsr/cdi/OptionalCDIProcessor.java @@ -15,15 +15,17 @@ @SupportedAnnotationTypes( "javax.inject.*" ) public class OptionalCDIProcessor extends AbstractProcessor { - private static final String DEFAULT_PROCESSOR = "trip.spi.inject.ProvidedClassesProcessor"; + private static final String DEFAULT_PROCESSOR = "trip.spi.processor.SPIProcessor"; AbstractProcessor cdiProcessor; @Override public synchronized void init(ProcessingEnvironment processingEnv) { super.init(processingEnv); - if ( isClassPresent(DEFAULT_PROCESSOR) ) + if ( isClassPresent( DEFAULT_PROCESSOR ) ) { cdiProcessor = newInstanceOf("trip.jsr.cdi.CDIProcessor", AbstractProcessor.class); + cdiProcessor.init( processingEnv ); + } } @Override diff --git a/trip-jsr-330/source/trip/jsr/cdi/SingleElementProvider.java b/trip-jsr-330/source/trip/jsr/cdi/SingleElementProvider.java new file mode 100644 index 0000000..76b3f6b --- /dev/null +++ b/trip-jsr-330/source/trip/jsr/cdi/SingleElementProvider.java @@ -0,0 +1,16 @@ +package trip.jsr.cdi; + +import javax.inject.Provider; + +import lombok.RequiredArgsConstructor; + +@RequiredArgsConstructor +public class SingleElementProvider implements Provider { + + final T t; + + @Override + public T get() { + return t; + } +} diff --git a/trip-processor/source/META-INF/services/javax.annotation.processing.Processor b/trip-processor/source/META-INF/services/javax.annotation.processing.Processor index 4b0adb5..773ce94 100644 --- a/trip-processor/source/META-INF/services/javax.annotation.processing.Processor +++ b/trip-processor/source/META-INF/services/javax.annotation.processing.Processor @@ -1 +1 @@ -trip.spi.inject.ProvidedClassesProcessor \ No newline at end of file +trip.spi.processor.SPIProcessor \ No newline at end of file diff --git a/trip-processor/source/trip/spi/inject/GenerableClass.java b/trip-processor/source/trip/spi/processor/GenerableClass.java similarity index 74% rename from trip-processor/source/trip/spi/inject/GenerableClass.java rename to trip-processor/source/trip/spi/processor/GenerableClass.java index 68de2bf..dcd32ab 100644 --- a/trip-processor/source/trip/spi/inject/GenerableClass.java +++ b/trip-processor/source/trip/spi/processor/GenerableClass.java @@ -1,4 +1,4 @@ -package trip.spi.inject; +package trip.spi.processor; public interface GenerableClass { diff --git a/trip-processor/source/trip/spi/inject/NameTransformations.java b/trip-processor/source/trip/spi/processor/NameTransformations.java similarity index 83% rename from trip-processor/source/trip/spi/inject/NameTransformations.java rename to trip-processor/source/trip/spi/processor/NameTransformations.java index 322c79a..a99e0fd 100644 --- a/trip-processor/source/trip/spi/inject/NameTransformations.java +++ b/trip-processor/source/trip/spi/processor/NameTransformations.java @@ -1,4 +1,4 @@ -package trip.spi.inject; +package trip.spi.processor; public class NameTransformations { diff --git a/trip-processor/source/trip/spi/inject/ProducerImplementation.java b/trip-processor/source/trip/spi/processor/ProducerImplementation.java similarity index 97% rename from trip-processor/source/trip/spi/inject/ProducerImplementation.java rename to trip-processor/source/trip/spi/processor/ProducerImplementation.java index 6deb7a5..e9d30be 100644 --- a/trip-processor/source/trip/spi/inject/ProducerImplementation.java +++ b/trip-processor/source/trip/spi/processor/ProducerImplementation.java @@ -1,6 +1,6 @@ -package trip.spi.inject; +package trip.spi.processor; -import static trip.spi.inject.NameTransformations.stripGenericsFrom; +import static trip.spi.processor.NameTransformations.stripGenericsFrom; import java.util.List; diff --git a/trip-processor/source/trip/spi/inject/ProvidedClassesProcessor.java b/trip-processor/source/trip/spi/processor/SPIProcessor.java similarity index 94% rename from trip-processor/source/trip/spi/inject/ProvidedClassesProcessor.java rename to trip-processor/source/trip/spi/processor/SPIProcessor.java index cd0a455..a30c9c2 100644 --- a/trip-processor/source/trip/spi/inject/ProvidedClassesProcessor.java +++ b/trip-processor/source/trip/spi/processor/SPIProcessor.java @@ -1,4 +1,4 @@ -package trip.spi.inject; +package trip.spi.processor; import java.io.File; import java.io.FileWriter; @@ -32,14 +32,14 @@ import trip.spi.Singleton; import trip.spi.Stateless; import trip.spi.helpers.cache.ServiceLoader; -import trip.spi.inject.stateless.StatelessClass; -import trip.spi.inject.stateless.StatelessClassGenerator; +import trip.spi.processor.stateless.StatelessClass; +import trip.spi.processor.stateless.StatelessClassGenerator; import com.github.mustachejava.DefaultMustacheFactory; import com.github.mustachejava.Mustache; @SupportedAnnotationTypes( "trip.spi.*" ) -public class ProvidedClassesProcessor extends AbstractProcessor { +public class SPIProcessor extends AbstractProcessor { static final String EOL = "\n"; static final String SERVICES = "META-INF/services/"; @@ -108,15 +108,11 @@ public void processSingletons( final RoundEnvironment roundEnv, Class " + superinterfaceOrClass.toString() ); + if ( superinterfaceOrClass != null ) memorizeAServiceImplementation( new SingletonImplementation( superinterfaceOrClass.toString(), implementationClass ) ); - }else { - System.out.println( "MEM ALL INTERFACES FOR: " + implementationClass ); - for ( final TypeMirror interfaceType : type.getInterfaces() ){ - System.out.println( " :: INTERFACES: " + interfaceType.toString() ); + else { + for ( final TypeMirror interfaceType : type.getInterfaces() ) memorizeAServiceImplementation( new SingletonImplementation(interfaceType.toString(), implementationClass) ); - } memorizeAServiceImplementation( new SingletonImplementation(implementationClass, implementationClass) ); } } diff --git a/trip-processor/source/trip/spi/inject/SingletonImplementation.java b/trip-processor/source/trip/spi/processor/SingletonImplementation.java similarity index 93% rename from trip-processor/source/trip/spi/inject/SingletonImplementation.java rename to trip-processor/source/trip/spi/processor/SingletonImplementation.java index 5f8729f..dac86c9 100644 --- a/trip-processor/source/trip/spi/inject/SingletonImplementation.java +++ b/trip-processor/source/trip/spi/processor/SingletonImplementation.java @@ -1,6 +1,6 @@ -package trip.spi.inject; +package trip.spi.processor; -import static trip.spi.inject.NameTransformations.stripGenericsFrom; +import static trip.spi.processor.NameTransformations.stripGenericsFrom; import java.lang.annotation.Annotation; import java.util.ArrayList; @@ -43,7 +43,10 @@ public static String getProvidedServiceClassAsString( final TypeElement type ) { } public static String getProvidedServiceClassAsStringOrNull( final TypeElement type ) { - return getProvidedServiceClassAsString(type, null); + String string = getProvidedServiceClassAsString( type, null ); + if ( string == null && ( isAnnotatedForSingleton( type ) || isAnnotatedForStateless( type ) ) ) + string = type.asType().toString(); + return string; } private static String getProvidedServiceClassAsString(final TypeElement type, String defaultValue) { @@ -59,8 +62,6 @@ public static TypeMirror getProvidedServiceClass( final TypeElement type ) { foundType = getProvidedServiceClassForStateless( type ); if ( foundType == null && isAnnotatedForSingleton( type ) ) foundType = getProvidedServiceClassForSingleton( type ); - if ( foundType == null && (isAnnotatedForSingleton( type ) || isAnnotatedForStateless( type ) ) ) - foundType = type.asType(); return foundType; } diff --git a/trip-processor/source/trip/spi/inject/stateless/ExposedMethod.java b/trip-processor/source/trip/spi/processor/stateless/ExposedMethod.java similarity index 98% rename from trip-processor/source/trip/spi/inject/stateless/ExposedMethod.java rename to trip-processor/source/trip/spi/processor/stateless/ExposedMethod.java index 72cf7f0..577dbc0 100644 --- a/trip-processor/source/trip/spi/inject/stateless/ExposedMethod.java +++ b/trip-processor/source/trip/spi/processor/stateless/ExposedMethod.java @@ -1,4 +1,4 @@ -package trip.spi.inject.stateless; +package trip.spi.processor.stateless; import java.util.ArrayList; import java.util.List; diff --git a/trip-processor/source/trip/spi/inject/stateless/StatelessClass.java b/trip-processor/source/trip/spi/processor/stateless/StatelessClass.java similarity index 97% rename from trip-processor/source/trip/spi/inject/stateless/StatelessClass.java rename to trip-processor/source/trip/spi/processor/stateless/StatelessClass.java index 8e5914b..e1ac8f8 100644 --- a/trip-processor/source/trip/spi/inject/stateless/StatelessClass.java +++ b/trip-processor/source/trip/spi/processor/stateless/StatelessClass.java @@ -1,4 +1,4 @@ -package trip.spi.inject.stateless; +package trip.spi.processor.stateless; import java.lang.annotation.Annotation; import java.util.ArrayList; @@ -14,8 +14,8 @@ import trip.spi.PostConstruct; import trip.spi.PreDestroy; -import trip.spi.inject.GenerableClass; -import trip.spi.inject.SingletonImplementation; +import trip.spi.processor.GenerableClass; +import trip.spi.processor.SingletonImplementation; public class StatelessClass implements GenerableClass { diff --git a/trip-processor/source/trip/spi/inject/stateless/StatelessClassGenerator.java b/trip-processor/source/trip/spi/processor/stateless/StatelessClassGenerator.java similarity index 92% rename from trip-processor/source/trip/spi/inject/stateless/StatelessClassGenerator.java rename to trip-processor/source/trip/spi/processor/stateless/StatelessClassGenerator.java index 1c4d20a..84c955c 100644 --- a/trip-processor/source/trip/spi/inject/stateless/StatelessClassGenerator.java +++ b/trip-processor/source/trip/spi/processor/stateless/StatelessClassGenerator.java @@ -1,4 +1,4 @@ -package trip.spi.inject.stateless; +package trip.spi.processor.stateless; import java.io.Writer; diff --git a/trip-processor/tests/trip/spi/inject/stateless/StatelessClassGeneratorTest.java b/trip-processor/tests/trip/spi/inject/stateless/StatelessClassGeneratorTest.java index 8aa677f..75c4c64 100644 --- a/trip-processor/tests/trip/spi/inject/stateless/StatelessClassGeneratorTest.java +++ b/trip-processor/tests/trip/spi/inject/stateless/StatelessClassGeneratorTest.java @@ -11,6 +11,10 @@ import org.junit.Test; +import trip.spi.processor.stateless.ExposedMethod; +import trip.spi.processor.stateless.StatelessClass; +import trip.spi.processor.stateless.StatelessClassGenerator; + public class StatelessClassGeneratorTest { @Test From 01a8019def8a35e8bea4caac09d6ba980aa305ce Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 16 Jul 2015 09:59:45 -0300 Subject: [PATCH 12/26] Closes #34 --- trip-core/source/trip/spi/PostConstruct.java | 16 ------- trip-core/source/trip/spi/PreDestroy.java | 16 ------- .../trip/spi/helpers/ProvidableClass.java | 47 ++++++++++++++++++- ...onstructAndPreDestroyStatelessService.java | 21 ++------- .../PostConstructorSingletonService.java | 16 +++++++ ...ructAndPreDestroyStatelessServiceTest.java | 26 ++++++---- .../source/META-INF/stateless-class.mustache | 10 +--- .../processor/stateless/StatelessClass.java | 6 +-- .../tests/stateless-class-exposing-class.txt | 9 +--- .../stateless-class-exposing-interface.txt | 9 +--- 10 files changed, 88 insertions(+), 88 deletions(-) delete mode 100644 trip-core/source/trip/spi/PostConstruct.java delete mode 100644 trip-core/source/trip/spi/PreDestroy.java create mode 100644 trip-integration-tests/source/trip/spi/tests/PostConstructorSingletonService.java diff --git a/trip-core/source/trip/spi/PostConstruct.java b/trip-core/source/trip/spi/PostConstruct.java deleted file mode 100644 index ee77252..0000000 --- a/trip-core/source/trip/spi/PostConstruct.java +++ /dev/null @@ -1,16 +0,0 @@ -package trip.spi; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -@Retention( RetentionPolicy.RUNTIME ) -@Target( { ElementType.METHOD, ElementType.TYPE } ) -public @interface PostConstruct { - - /** - * The name that identifies the service. - */ - String name() default ""; -} diff --git a/trip-core/source/trip/spi/PreDestroy.java b/trip-core/source/trip/spi/PreDestroy.java deleted file mode 100644 index fed5112..0000000 --- a/trip-core/source/trip/spi/PreDestroy.java +++ /dev/null @@ -1,16 +0,0 @@ -package trip.spi; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -@Retention( RetentionPolicy.RUNTIME ) -@Target( { ElementType.METHOD, ElementType.TYPE } ) -public @interface PreDestroy { - - /** - * The name that identifies the service. - */ - String name() default ""; -} diff --git a/trip-core/source/trip/spi/helpers/ProvidableClass.java b/trip-core/source/trip/spi/helpers/ProvidableClass.java index ad32237..499644d 100644 --- a/trip-core/source/trip/spi/helpers/ProvidableClass.java +++ b/trip-core/source/trip/spi/helpers/ProvidableClass.java @@ -2,10 +2,15 @@ import java.lang.annotation.Annotation; import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.List; +import java.util.function.Consumer; + +import javax.annotation.PostConstruct; import lombok.RequiredArgsConstructor; import trip.spi.GeneratedFromStatelessService; @@ -17,15 +22,32 @@ public class ProvidableClass { final Class targetClazz; final Iterable fields; + final Consumer postConstructor; public void provide( Object instance, ServiceProvider provider ) throws ServiceProviderException, IllegalArgumentException, IllegalAccessException { + postConstructor.accept(instance); for ( final ProvidableField field : fields ) field.provide( instance, provider ); } public static ProvidableClass wrap( QualifierExtractor extractor, Class targetClazz ) { - return new ProvidableClass( targetClazz, readClassProvidableFields( extractor, targetClazz ) ); + return new ProvidableClass( + targetClazz, + readClassProvidableFields( extractor, targetClazz ), + readPostConstructor(targetClazz)); + } + + static Consumer readPostConstructor( Class targetClazz ){ + Method postConstructor = null; + for ( final Method method : targetClazz.getMethods() ) + if ( method.isAnnotationPresent(PostConstruct.class) ){ + postConstructor = method; + break; + } + return postConstructor != null + ? new PostConstructorMethod(postConstructor) + : EmptyMethod.INSTANCE; } static Iterable readClassProvidableFields( QualifierExtractor extractor, Class targetClazz ) { @@ -58,3 +80,26 @@ private static Collection> extractQualifiersFromAvoi return extractor.extractQualifiersFrom(field); } } + +class EmptyMethod implements Consumer { + + static final Consumer INSTANCE = new EmptyMethod(); + + @Override + public void accept(Object t) {} +} + +@RequiredArgsConstructor +class PostConstructorMethod implements Consumer { + + final Method method; + + @Override + public void accept(Object target) { + try { + method.invoke(target); + } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { + throw new ServiceProviderException(e); + } + } +} \ No newline at end of file diff --git a/trip-integration-tests/source/trip/spi/tests/PostConstructAndPreDestroyStatelessService.java b/trip-integration-tests/source/trip/spi/tests/PostConstructAndPreDestroyStatelessService.java index 6a104b9..5cebdd8 100644 --- a/trip-integration-tests/source/trip/spi/tests/PostConstructAndPreDestroyStatelessService.java +++ b/trip-integration-tests/source/trip/spi/tests/PostConstructAndPreDestroyStatelessService.java @@ -9,22 +9,12 @@ public class PostConstructAndPreDestroyStatelessService { @javax.annotation.PostConstruct public void postConstructorJava() { - status.calledPostContructJavaAnnotation = true; - } - - @trip.spi.PostConstruct - public void postConstructorTrip() { - status.calledPostContructTrip = true; + status.calledPostContructJavaAnnotation++; } @javax.annotation.PreDestroy public void preDestroyJava() { - status.calledPreDestroyJavaAnnotation = true; - } - - @trip.spi.PreDestroy - public void preDestroyTrip() { - status.calledPreDestroyTrip = true; + status.calledPreDestroyJavaAnnotation++; } public Status getStatus() { @@ -33,9 +23,6 @@ public Status getStatus() { } class Status { - - boolean calledPreDestroyJavaAnnotation; - boolean calledPreDestroyTrip; - boolean calledPostContructJavaAnnotation; - boolean calledPostContructTrip; + int calledPreDestroyJavaAnnotation = 0; + int calledPostContructJavaAnnotation = 0; } \ No newline at end of file diff --git a/trip-integration-tests/source/trip/spi/tests/PostConstructorSingletonService.java b/trip-integration-tests/source/trip/spi/tests/PostConstructorSingletonService.java new file mode 100644 index 0000000..3aef979 --- /dev/null +++ b/trip-integration-tests/source/trip/spi/tests/PostConstructorSingletonService.java @@ -0,0 +1,16 @@ +package trip.spi.tests; + +import lombok.Getter; +import trip.spi.Singleton; + +@Singleton +public class PostConstructorSingletonService { + + @Getter + final Status status = new Status(); + + @javax.annotation.PostConstruct + public void postConstructorJava() { + status.calledPostContructJavaAnnotation++; + } +} diff --git a/trip-integration-tests/tests/trip/spi/tests/PostConstructAndPreDestroyStatelessServiceTest.java b/trip-integration-tests/tests/trip/spi/tests/PostConstructAndPreDestroyStatelessServiceTest.java index 88cdc3c..db6abb4 100644 --- a/trip-integration-tests/tests/trip/spi/tests/PostConstructAndPreDestroyStatelessServiceTest.java +++ b/trip-integration-tests/tests/trip/spi/tests/PostConstructAndPreDestroyStatelessServiceTest.java @@ -1,19 +1,22 @@ package trip.spi.tests; -import static org.junit.Assert.assertTrue; +import static org.junit.Assert.assertEquals; import lombok.val; import org.junit.Before; import org.junit.Test; -import trip.spi.Provided; import trip.spi.DefaultServiceProvider; +import trip.spi.Provided; import trip.spi.ServiceProviderException; public class PostConstructAndPreDestroyStatelessServiceTest { @Provided - PostConstructAndPreDestroyStatelessService service; + PostConstructAndPreDestroyStatelessService stateless; + + @Provided + PostConstructorSingletonService singleton; @Before public void provideDependencies() throws ServiceProviderException { @@ -21,11 +24,16 @@ public void provideDependencies() throws ServiceProviderException { } @Test - public void ensureThatCalledAllCallbacks() { - val status = service.getStatus(); - assertTrue( status.calledPostContructJavaAnnotation ); - assertTrue( status.calledPreDestroyJavaAnnotation ); - assertTrue( status.calledPostContructTrip ); - assertTrue( status.calledPreDestroyTrip ); + public void ensureThatCalledAllCallbacksOnStateless() { + val status = stateless.getStatus(); + assertEquals( 1, status.calledPostContructJavaAnnotation ); + assertEquals( 1, status.calledPreDestroyJavaAnnotation ); + } + + @Test + public void ensureThatCalledOnlyPostConstructorCallbacksOnSingleton() { + val status = singleton.getStatus(); + assertEquals( 1, status.calledPostContructJavaAnnotation ); + assertEquals( 0, status.calledPreDestroyJavaAnnotation ); } } diff --git a/trip-processor/source/META-INF/stateless-class.mustache b/trip-processor/source/META-INF/stateless-class.mustache index ccf7672..4054735 100644 --- a/trip-processor/source/META-INF/stateless-class.mustache +++ b/trip-processor/source/META-INF/stateless-class.mustache @@ -16,7 +16,6 @@ public class {{typeName}}Stateless{{identifaction}} public {{{returnType}}} {{name}}( {{{parametersWithTypesAsString}}} ) { try { final {{{implementationCanonicalName}}} instance = newInstance(); - postConstruct{{typeName}}{{identifaction}}( instance ); try { {{#returnable}} final {{{returnType}}} returnValue = instance.{{name}}( {{parametersAsString}} ); @@ -39,14 +38,7 @@ public class {{typeName}}Stateless{{identifaction}} provider.provideOn( instance ); return instance; } - - void postConstruct{{typeName}}{{identifaction}}( - final {{{implementationCanonicalName}}} instance ) throws Throwable { - {{#postConstructMethods}} - instance.{{name}}(); - {{/postConstructMethods}} - } - + void preDestroy{{typeName}}{{identifaction}}( final {{{implementationCanonicalName}}} instance ) throws Throwable { {{#preDestroyMethods}} diff --git a/trip-processor/source/trip/spi/processor/stateless/StatelessClass.java b/trip-processor/source/trip/spi/processor/stateless/StatelessClass.java index e1ac8f8..49341bd 100644 --- a/trip-processor/source/trip/spi/processor/stateless/StatelessClass.java +++ b/trip-processor/source/trip/spi/processor/stateless/StatelessClass.java @@ -12,8 +12,6 @@ import javax.lang.model.type.DeclaredType; import javax.lang.model.type.TypeMirror; -import trip.spi.PostConstruct; -import trip.spi.PreDestroy; import trip.spi.processor.GenerableClass; import trip.spi.processor.SingletonImplementation; @@ -158,8 +156,8 @@ public static StatelessClass from( final TypeElement type ) { final List exposedMethods = retrieveExposedMethods( type ); return new StatelessClass( typeCanonicalName, implementationCanonicalName, exposedByClass, exposedMethods, - retrieveMethodsAnnotatedWith( type, PostConstruct.class, javax.annotation.PostConstruct.class ), - retrieveMethodsAnnotatedWith( type, PreDestroy.class, javax.annotation.PreDestroy.class ) ); + retrieveMethodsAnnotatedWith( type, javax.annotation.PostConstruct.class ), + retrieveMethodsAnnotatedWith( type, javax.annotation.PreDestroy.class ) ); } public static boolean isImplementingClass( final String typeCanonicalName, TypeElement type ) { diff --git a/trip-processor/tests/stateless-class-exposing-class.txt b/trip-processor/tests/stateless-class-exposing-class.txt index 2cef17e..844a5c4 100644 --- a/trip-processor/tests/stateless-class-exposing-class.txt +++ b/trip-processor/tests/stateless-class-exposing-class.txt @@ -10,7 +10,6 @@ public class ServiceFromInterfaceStateless3407109653 public void voidMethod( ) { try { final sample.project.ServiceFromInterface instance = newInstance(); - postConstructServiceFromInterface3407109653( instance ); try { instance.voidMethod( ); } finally { @@ -24,7 +23,6 @@ public class ServiceFromInterfaceStateless3407109653 public Long sum( Double arg0,Integer arg1 ) { try { final sample.project.ServiceFromInterface instance = newInstance(); - postConstructServiceFromInterface3407109653( instance ); try { final Long returnValue = instance.sum( arg0,arg1 ); return returnValue; @@ -41,12 +39,7 @@ public class ServiceFromInterfaceStateless3407109653 provider.provideOn( instance ); return instance; } - - void postConstructServiceFromInterface3407109653( - final sample.project.ServiceFromInterface instance ) throws Throwable { - instance.sum(); - } - + void preDestroyServiceFromInterface3407109653( final sample.project.ServiceFromInterface instance ) throws Throwable { instance.voidMethod(); diff --git a/trip-processor/tests/stateless-class-exposing-interface.txt b/trip-processor/tests/stateless-class-exposing-interface.txt index e4bbccc..fdd3c44 100644 --- a/trip-processor/tests/stateless-class-exposing-interface.txt +++ b/trip-processor/tests/stateless-class-exposing-interface.txt @@ -10,7 +10,6 @@ public class InterfaceStateless1612123513 public void voidMethod( ) { try { final sample.project.ServiceFromInterface instance = newInstance(); - postConstructInterface1612123513( instance ); try { instance.voidMethod( ); } finally { @@ -24,7 +23,6 @@ public class InterfaceStateless1612123513 public Long sum( Double arg0,Integer arg1 ) { try { final sample.project.ServiceFromInterface instance = newInstance(); - postConstructInterface1612123513( instance ); try { final Long returnValue = instance.sum( arg0,arg1 ); return returnValue; @@ -41,12 +39,7 @@ public class InterfaceStateless1612123513 provider.provideOn( instance ); return instance; } - - void postConstructInterface1612123513( - final sample.project.ServiceFromInterface instance ) throws Throwable { - instance.sum(); - } - + void preDestroyInterface1612123513( final sample.project.ServiceFromInterface instance ) throws Throwable { instance.voidMethod(); From d984b5777af20d6434dda8a74b236d012d991564 Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 16 Jul 2015 10:11:22 -0300 Subject: [PATCH 13/26] Regression fixed: onStartup methods was depending on DefaultServiceProvider instead of ServiceProvider --- trip-core/source/trip/spi/StartupListener.java | 2 +- .../tests/trip/spi/startup/ConfigurationStartupListener.java | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/trip-core/source/trip/spi/StartupListener.java b/trip-core/source/trip/spi/StartupListener.java index 3bcf134..445139d 100644 --- a/trip-core/source/trip/spi/StartupListener.java +++ b/trip-core/source/trip/spi/StartupListener.java @@ -4,5 +4,5 @@ public interface StartupListener { default void beforeProducersReady( final ServiceProvider provider ){} - void onStartup( final DefaultServiceProvider provider ); + void onStartup( final ServiceProvider provider ); } diff --git a/trip-core/tests/trip/spi/startup/ConfigurationStartupListener.java b/trip-core/tests/trip/spi/startup/ConfigurationStartupListener.java index 78c59a2..8da7e6e 100644 --- a/trip-core/tests/trip/spi/startup/ConfigurationStartupListener.java +++ b/trip-core/tests/trip/spi/startup/ConfigurationStartupListener.java @@ -1,6 +1,6 @@ package trip.spi.startup; -import trip.spi.DefaultServiceProvider; +import trip.spi.ServiceProvider; import trip.spi.StartupListener; public class ConfigurationStartupListener implements StartupListener { @@ -8,7 +8,7 @@ public class ConfigurationStartupListener implements StartupListener { public static final String EXPECTED_CONFIG = "I was Injected"; @Override - public void onStartup( final DefaultServiceProvider provider ) { + public void onStartup( final ServiceProvider provider ) { provider.providerFor( Configuration.class, new Configuration( EXPECTED_CONFIG ) ); } } From 9478f8841cdf0dce0653c69cb76f5fdfbce32ad2 Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 16 Jul 2015 19:03:42 -0300 Subject: [PATCH 14/26] Addded developer friendly messages to the compilation log --- .../spi/helpers/SingleElementProvidableField.java | 2 +- .../source/trip/spi/helpers/filter/AnyObject.java | 5 +++++ .../trip/spi/helpers/filter/ChainedCondition.java | 13 +++++++++++++ .../trip/spi/helpers/filter/IsAssignableFrom.java | 5 +++++ .../spi/helpers/filter/QualifierCondition.java | 5 +++++ .../source/trip/jsr/cdi/CDILoaderOfClasses.java | 1 + .../source/trip/jsr/cdi/OptionalCDIProcessor.java | 9 ++++++++- .../source/trip/spi/processor/SPIProcessor.java | 14 ++++++++++---- 8 files changed, 48 insertions(+), 6 deletions(-) diff --git a/trip-core/source/trip/spi/helpers/SingleElementProvidableField.java b/trip-core/source/trip/spi/helpers/SingleElementProvidableField.java index b828635..0469940 100644 --- a/trip-core/source/trip/spi/helpers/SingleElementProvidableField.java +++ b/trip-core/source/trip/spi/helpers/SingleElementProvidableField.java @@ -30,7 +30,7 @@ public void provide( final Object instance, final ServiceProvider provider ) throws ServiceProviderException, IllegalArgumentException, IllegalAccessException { final Object value = provider.load( fieldType, condition, providerContext ); if ( value == null ) - log.warning( "No data found for " + fieldType.getCanonicalName() ); + log.warning( "No data found for " + fieldType.getCanonicalName() + ". Condition: " + condition ); set( instance, value ); } diff --git a/trip-core/source/trip/spi/helpers/filter/AnyObject.java b/trip-core/source/trip/spi/helpers/filter/AnyObject.java index ad25e58..830df49 100644 --- a/trip-core/source/trip/spi/helpers/filter/AnyObject.java +++ b/trip-core/source/trip/spi/helpers/filter/AnyObject.java @@ -16,4 +16,9 @@ public static Condition instance() { public boolean check(Object object) { return true; } + + @Override + public String toString() { + return "AnyObject"; + } } diff --git a/trip-core/source/trip/spi/helpers/filter/ChainedCondition.java b/trip-core/source/trip/spi/helpers/filter/ChainedCondition.java index 0a78f38..9bfb52d 100644 --- a/trip-core/source/trip/spi/helpers/filter/ChainedCondition.java +++ b/trip-core/source/trip/spi/helpers/filter/ChainedCondition.java @@ -22,4 +22,17 @@ public boolean check( final T object ) { public void add( final Condition condition ) { conditions.add( condition ); } + + @Override + public String toString() { + final StringBuilder buffer = new StringBuilder(); + boolean isFirst = true; + for ( final Condition c : conditions ){ + if ( !isFirst ) + buffer.append(" AND "); + buffer.append(c.toString()); + isFirst=false; + } + return buffer.toString(); + } } diff --git a/trip-core/source/trip/spi/helpers/filter/IsAssignableFrom.java b/trip-core/source/trip/spi/helpers/filter/IsAssignableFrom.java index 3d70166..847f2fe 100644 --- a/trip-core/source/trip/spi/helpers/filter/IsAssignableFrom.java +++ b/trip-core/source/trip/spi/helpers/filter/IsAssignableFrom.java @@ -14,4 +14,9 @@ public boolean check( Object object ) { || expectedClass.isAssignableFrom( object.getClass() ) || ProducerFactory.class.isInstance( object ); } + + @Override + public String toString() { + return "IsAssignableFrom(" + expectedClass.getCanonicalName() + ")"; + } } diff --git a/trip-core/source/trip/spi/helpers/filter/QualifierCondition.java b/trip-core/source/trip/spi/helpers/filter/QualifierCondition.java index ba85fbb..4380226 100644 --- a/trip-core/source/trip/spi/helpers/filter/QualifierCondition.java +++ b/trip-core/source/trip/spi/helpers/filter/QualifierCondition.java @@ -18,4 +18,9 @@ public boolean check(T object) { return false; return true; } + + @Override + public String toString() { + return "Qualifiers(" + qualifiers + ")"; + } } diff --git a/trip-jsr-330/source/trip/jsr/cdi/CDILoaderOfClasses.java b/trip-jsr-330/source/trip/jsr/cdi/CDILoaderOfClasses.java index 07ed321..f35458f 100644 --- a/trip-jsr-330/source/trip/jsr/cdi/CDILoaderOfClasses.java +++ b/trip-jsr-330/source/trip/jsr/cdi/CDILoaderOfClasses.java @@ -9,6 +9,7 @@ static Class loadClass( String className ){ try { return (Class)Class.forName(className); } catch (final ClassNotFoundException e) { + e.printStackTrace(); return null; } } diff --git a/trip-jsr-330/source/trip/jsr/cdi/OptionalCDIProcessor.java b/trip-jsr-330/source/trip/jsr/cdi/OptionalCDIProcessor.java index ec74746..09e7614 100644 --- a/trip-jsr-330/source/trip/jsr/cdi/OptionalCDIProcessor.java +++ b/trip-jsr-330/source/trip/jsr/cdi/OptionalCDIProcessor.java @@ -11,6 +11,7 @@ import javax.annotation.processing.SupportedAnnotationTypes; import javax.lang.model.SourceVersion; import javax.lang.model.element.TypeElement; +import javax.tools.Diagnostic.Kind; @SupportedAnnotationTypes( "javax.inject.*" ) public class OptionalCDIProcessor extends AbstractProcessor { @@ -25,7 +26,13 @@ public synchronized void init(ProcessingEnvironment processingEnv) { if ( isClassPresent( DEFAULT_PROCESSOR ) ) { cdiProcessor = newInstanceOf("trip.jsr.cdi.CDIProcessor", AbstractProcessor.class); cdiProcessor.init( processingEnv ); - } + } else + warn( "CDIProcessor disabled" ); + } + + private void warn( String msg ){ + System.out.println( "[WARN] " + msg ); + processingEnv.getMessager().printMessage( Kind.WARNING, msg ); } @Override diff --git a/trip-processor/source/trip/spi/processor/SPIProcessor.java b/trip-processor/source/trip/spi/processor/SPIProcessor.java index a30c9c2..f01a38e 100644 --- a/trip-processor/source/trip/spi/processor/SPIProcessor.java +++ b/trip-processor/source/trip/spi/processor/SPIProcessor.java @@ -88,7 +88,7 @@ void memorizeAServiceImplementation( final StatelessClass clazz ) throws IOExcep void createAStatelessClassFrom( final StatelessClass clazz ) throws IOException { final String name = clazz.getGeneratedClassCanonicalName(); if ( !classExists( name ) ) { - log( "Generating " + name ); + info("Generating " + name); final JavaFileObject sourceFile = filer().createSourceFile( name ); final Writer writer = sourceFile.openWriter(); this.statelessClassGenerator.write( clazz, writer ); @@ -151,7 +151,7 @@ public void processProducers( final RoundEnvironment roundEnv, Class " + implementation ); + System.out.println( "[INFO] > " + implementation ); log( "Exposing " + implementation + " as " + interfaceClass ); resource.write( implementation + EOL ); } @@ -216,6 +217,11 @@ Filer filer() { return this.processingEnv.getFiler(); } + private void info(final String msg) { + System.out.println( "[INFO] " + msg ); + log( msg ); + } + private void log( final String msg ) { processingEnv.getMessager().printMessage( Kind.NOTE, msg ); } From b59f7d4365622d87c3c0ae2b83a784859dcc78f9 Mon Sep 17 00:00:00 2001 From: Miere Liniel Teixeira Date: Thu, 16 Jul 2015 23:43:44 -0300 Subject: [PATCH 15/26] Closes #35 --- .../trip/spi/DefaultServiceProvider.java | 15 +++++---- .../source/trip/spi/SingletonContext.java | 5 ++- .../helpers/{cache => }/LazyClassReader.java | 2 +- .../trip/spi/helpers/ServiceLoader.java | 17 ++++++++++ .../spi/helpers/cache/CachedIterable.java | 32 ------------------- .../trip/spi/helpers/cache/ServiceLoader.java | 18 ----------- .../SingleAndManyServicesInjectionTest.java | 11 ++++++- 7 files changed, 39 insertions(+), 61 deletions(-) rename trip-core/source/trip/spi/helpers/{cache => }/LazyClassReader.java (99%) create mode 100644 trip-core/source/trip/spi/helpers/ServiceLoader.java delete mode 100644 trip-core/source/trip/spi/helpers/cache/CachedIterable.java delete mode 100644 trip-core/source/trip/spi/helpers/cache/ServiceLoader.java diff --git a/trip-core/source/trip/spi/DefaultServiceProvider.java b/trip-core/source/trip/spi/DefaultServiceProvider.java index cbc5f09..8ae7f16 100644 --- a/trip-core/source/trip/spi/DefaultServiceProvider.java +++ b/trip-core/source/trip/spi/DefaultServiceProvider.java @@ -4,6 +4,7 @@ import static trip.spi.helpers.filter.Filter.first; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.ServiceConfigurationError; @@ -12,8 +13,8 @@ import trip.spi.helpers.ProducerFactoryMap; import trip.spi.helpers.ProvidableClass; import trip.spi.helpers.QualifierExtractor; +import trip.spi.helpers.ServiceLoader; import trip.spi.helpers.SingleObjectIterable; -import trip.spi.helpers.cache.ServiceLoader; import trip.spi.helpers.filter.AnyObject; import trip.spi.helpers.filter.Condition; @@ -119,15 +120,17 @@ protected Iterable loadAllServicesImplementingTheInterface( final Class Iterable loadServiceProvidersFor( final Class interfaceClazz ) { - final Iterable> iterableInterfaces = loadClassesImplementing( interfaceClazz ); - return singletonContext.instantiate(iterableInterfaces); + final List> iterableInterfaces = loadClassesImplementing( interfaceClazz ); + if ( !iterableInterfaces.isEmpty() ) + return singletonContext.instantiate( iterableInterfaces ); + return new SingleObjectIterable<>( singletonContext.instantiate( interfaceClazz ) ); } - public Iterable> loadClassesImplementing( final Class interfaceClazz ) { - Iterable> implementations = (Iterable)implementedClasses.get( interfaceClazz ); + public List> loadClassesImplementing( final Class interfaceClazz ) { + List> implementations = (List)implementedClasses.get( interfaceClazz ); if ( implementations == null ) synchronized ( implementedClasses ) { - implementations = (Iterable)implementedClasses.get( interfaceClazz ); + implementations = (List)implementedClasses.get( interfaceClazz ); if ( implementations == null ) { implementations = ServiceLoader.loadImplementationsFor( interfaceClazz ); implementedClasses.put( (Class)interfaceClazz, (Iterable)implementations ); diff --git a/trip-core/source/trip/spi/SingletonContext.java b/trip-core/source/trip/spi/SingletonContext.java index e4ddd03..f1adb03 100644 --- a/trip-core/source/trip/spi/SingletonContext.java +++ b/trip-core/source/trip/spi/SingletonContext.java @@ -20,16 +20,15 @@ public Iterable instantiate( Iterable> classes ){ for ( final Class clazz : classes ){ T object = (T)cache.get(clazz); if ( object == null ) - object = instantiate(clazz); + cache.put( clazz, object = instantiate( clazz ) ); list.add(object); } return list; } - T instantiate( Class clazz ) { + public T instantiate( Class clazz ) { try { final T instance = clazz.newInstance(); - cache.put( clazz, instance); return instance; } catch ( final IllegalAccessException | InstantiationException cause ) { log.warning( cause.getMessage() ); diff --git a/trip-core/source/trip/spi/helpers/cache/LazyClassReader.java b/trip-core/source/trip/spi/helpers/LazyClassReader.java similarity index 99% rename from trip-core/source/trip/spi/helpers/cache/LazyClassReader.java rename to trip-core/source/trip/spi/helpers/LazyClassReader.java index 236ccb8..abcb213 100644 --- a/trip-core/source/trip/spi/helpers/cache/LazyClassReader.java +++ b/trip-core/source/trip/spi/helpers/LazyClassReader.java @@ -1,4 +1,4 @@ -package trip.spi.helpers.cache; +package trip.spi.helpers; import java.io.BufferedReader; import java.io.FileNotFoundException; diff --git a/trip-core/source/trip/spi/helpers/ServiceLoader.java b/trip-core/source/trip/spi/helpers/ServiceLoader.java new file mode 100644 index 0000000..b84f21e --- /dev/null +++ b/trip-core/source/trip/spi/helpers/ServiceLoader.java @@ -0,0 +1,17 @@ +package trip.spi.helpers; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +public abstract class ServiceLoader { + + public static List> loadImplementationsFor( Class clazz ) { + final ClassLoader cl = Thread.currentThread().getContextClassLoader(); + final Iterator> reader = new LazyClassReader( clazz, cl ); + final List> list = new ArrayList<>(); + while ( reader.hasNext() ) + list.add( reader.next() ); + return list; + } +} diff --git a/trip-core/source/trip/spi/helpers/cache/CachedIterable.java b/trip-core/source/trip/spi/helpers/cache/CachedIterable.java deleted file mode 100644 index f9112ba..0000000 --- a/trip-core/source/trip/spi/helpers/cache/CachedIterable.java +++ /dev/null @@ -1,32 +0,0 @@ -package trip.spi.helpers.cache; - -import java.util.ArrayList; -import java.util.Iterator; -import java.util.List; - -import lombok.RequiredArgsConstructor; - -@RequiredArgsConstructor -public class CachedIterable implements Iterable { - - final Iterator cachedProducer; - Iterable cache; - - @Override - public Iterator iterator() { - if ( cache == null ) - cache = createCache(); - return cache.iterator(); - } - - public Iterable createCache() { - final List cache = new ArrayList(); - while( cachedProducer.hasNext() ) - try { - cache.add( cachedProducer.next() ); - } catch ( final IllegalStateException cause ) { - continue; - } - return cache; - } -} diff --git a/trip-core/source/trip/spi/helpers/cache/ServiceLoader.java b/trip-core/source/trip/spi/helpers/cache/ServiceLoader.java deleted file mode 100644 index acd23d3..0000000 --- a/trip-core/source/trip/spi/helpers/cache/ServiceLoader.java +++ /dev/null @@ -1,18 +0,0 @@ -package trip.spi.helpers.cache; - -import java.util.Iterator; - -public abstract class ServiceLoader { - - public static CachedIterable> loadImplementationsFor( Class clazz ) { - final ClassLoader cl = Thread.currentThread().getContextClassLoader(); - final Iterator> reader = new LazyClassReader(clazz, cl); - return new CachedIterable>(reader); - } - - public static CachedIterable> loadImplementationsFor( String interfaceCanonicalName ) { - final ClassLoader cl = Thread.currentThread().getContextClassLoader(); - final Iterator> reader = new LazyClassReader( interfaceCanonicalName, cl ); - return new CachedIterable>( reader ); - } -} diff --git a/trip-core/tests/trip/spi/SingleAndManyServicesInjectionTest.java b/trip-core/tests/trip/spi/SingleAndManyServicesInjectionTest.java index 6926206..cca7f65 100644 --- a/trip-core/tests/trip/spi/SingleAndManyServicesInjectionTest.java +++ b/trip-core/tests/trip/spi/SingleAndManyServicesInjectionTest.java @@ -2,6 +2,7 @@ import static org.hamcrest.CoreMatchers.instanceOf; import static org.hamcrest.CoreMatchers.notNullValue; +import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertThat; import static org.junit.Assert.fail; import lombok.Getter; @@ -11,7 +12,7 @@ public class SingleAndManyServicesInjectionTest { - static final ServiceProvider provider = new DefaultServiceProvider(); + final ServiceProvider provider = new DefaultServiceProvider(); @Test( timeout = 3000 ) public void applyStressTestOnMyAssertion() throws ServiceProviderException { @@ -54,6 +55,14 @@ void assertNotContains( Iterable iterable, Class clazz ) { if ( item.getClass().equals( clazz ) ) fail( "Iterable contains an object from type " + clazz.getCanonicalName() ); } + + @Test + public void ensureThatNonManagedClassesAreCreatedEveryTime() { + InjectableClass injectable = provider.load( InjectableClass.class ); + assertNotNull( injectable ); + assertPrintablesArePopulatedAsExpected( injectable ); + assertPrintableFoosArePopulatedAsExpected( injectable ); + } } @Getter From a17c725047337c2cd88718f5b191c2ef566f9345 Mon Sep 17 00:00:00 2001 From: Miere Liniel Teixeira Date: Fri, 17 Jul 2015 00:27:53 -0300 Subject: [PATCH 16/26] Regression fixed: ServiceLoader was missing a method --- .../trip/spi/DefaultServiceProvider.java | 6 +++- .../source/trip/spi/SingletonContext.java | 5 +--- .../trip/spi/helpers/EmptyIterable.java | 28 +++++++++++++++++++ .../trip/spi/helpers/ServiceLoader.java | 16 ++++++++++- .../trip/spi/processor/SPIProcessor.java | 2 +- 5 files changed, 50 insertions(+), 7 deletions(-) create mode 100644 trip-core/source/trip/spi/helpers/EmptyIterable.java diff --git a/trip-core/source/trip/spi/DefaultServiceProvider.java b/trip-core/source/trip/spi/DefaultServiceProvider.java index 8ae7f16..4f1f8ad 100644 --- a/trip-core/source/trip/spi/DefaultServiceProvider.java +++ b/trip-core/source/trip/spi/DefaultServiceProvider.java @@ -8,6 +8,7 @@ import java.util.Map; import java.util.ServiceConfigurationError; +import trip.spi.helpers.EmptyIterable; import trip.spi.helpers.EmptyProviderContext; import trip.spi.helpers.FieldQualifierExtractor; import trip.spi.helpers.ProducerFactoryMap; @@ -123,7 +124,10 @@ protected Iterable loadServiceProvidersFor( final List> iterableInterfaces = loadClassesImplementing( interfaceClazz ); if ( !iterableInterfaces.isEmpty() ) return singletonContext.instantiate( iterableInterfaces ); - return new SingleObjectIterable<>( singletonContext.instantiate( interfaceClazz ) ); + T instance = singletonContext.instantiate( interfaceClazz ); + if ( instance != null ) + return new SingleObjectIterable<>( instance ); + return EmptyIterable.instance(); } public List> loadClassesImplementing( final Class interfaceClazz ) { diff --git a/trip-core/source/trip/spi/SingletonContext.java b/trip-core/source/trip/spi/SingletonContext.java index f1adb03..c5bf421 100644 --- a/trip-core/source/trip/spi/SingletonContext.java +++ b/trip-core/source/trip/spi/SingletonContext.java @@ -6,10 +6,8 @@ import java.util.Map; import lombok.RequiredArgsConstructor; -import lombok.extern.java.Log; @RequiredArgsConstructor -@Log class SingletonContext { final Map, Object> cache = new HashMap<>(); @@ -31,8 +29,7 @@ public T instantiate( Class clazz ) { final T instance = clazz.newInstance(); return instance; } catch ( final IllegalAccessException | InstantiationException cause ) { - log.warning( cause.getMessage() ); - throw new IllegalStateException( cause ); + return null; } } } \ No newline at end of file diff --git a/trip-core/source/trip/spi/helpers/EmptyIterable.java b/trip-core/source/trip/spi/helpers/EmptyIterable.java new file mode 100644 index 0000000..5b7d565 --- /dev/null +++ b/trip-core/source/trip/spi/helpers/EmptyIterable.java @@ -0,0 +1,28 @@ +package trip.spi.helpers; + +import java.util.Iterator; + +@SuppressWarnings( { "rawtypes", "unchecked" } ) +public class EmptyIterable implements Iterable, Iterator { + + private final static EmptyIterable INSTANCE = new EmptyIterable(); + + public static EmptyIterable instance() { + return INSTANCE; + } + + @Override + public boolean hasNext() { + return false; + } + + @Override + public T next() { + return null; + } + + @Override + public Iterator iterator() { + return this; + } +} diff --git a/trip-core/source/trip/spi/helpers/ServiceLoader.java b/trip-core/source/trip/spi/helpers/ServiceLoader.java index b84f21e..51f2037 100644 --- a/trip-core/source/trip/spi/helpers/ServiceLoader.java +++ b/trip-core/source/trip/spi/helpers/ServiceLoader.java @@ -9,9 +9,23 @@ public abstract class ServiceLoader { public static List> loadImplementationsFor( Class clazz ) { final ClassLoader cl = Thread.currentThread().getContextClassLoader(); final Iterator> reader = new LazyClassReader( clazz, cl ); + return readAndConvertToList( reader ); + } + + public static List> loadImplementationsFor( String clazz ) { + final ClassLoader cl = Thread.currentThread().getContextClassLoader(); + final Iterator> reader = new LazyClassReader( clazz, cl ); + return readAndConvertToList( reader ); + } + + private static List> readAndConvertToList( final Iterator> reader ) { final List> list = new ArrayList<>(); while ( reader.hasNext() ) - list.add( reader.next() ); + try { + list.add( reader.next() ); + } catch ( IllegalStateException cause ) { + continue; + } return list; } } diff --git a/trip-processor/source/trip/spi/processor/SPIProcessor.java b/trip-processor/source/trip/spi/processor/SPIProcessor.java index f01a38e..223310a 100644 --- a/trip-processor/source/trip/spi/processor/SPIProcessor.java +++ b/trip-processor/source/trip/spi/processor/SPIProcessor.java @@ -31,7 +31,7 @@ import trip.spi.ProducerFactory; import trip.spi.Singleton; import trip.spi.Stateless; -import trip.spi.helpers.cache.ServiceLoader; +import trip.spi.helpers.ServiceLoader; import trip.spi.processor.stateless.StatelessClass; import trip.spi.processor.stateless.StatelessClassGenerator; From 3b29a7aa1581f8dab134b587504e638231de1b76 Mon Sep 17 00:00:00 2001 From: unknown Date: Fri, 17 Jul 2015 09:51:51 -0300 Subject: [PATCH 17/26] #34: post constructors should be called after injection process --- trip-core/source/trip/spi/helpers/ProvidableClass.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/trip-core/source/trip/spi/helpers/ProvidableClass.java b/trip-core/source/trip/spi/helpers/ProvidableClass.java index 499644d..5d3acaa 100644 --- a/trip-core/source/trip/spi/helpers/ProvidableClass.java +++ b/trip-core/source/trip/spi/helpers/ProvidableClass.java @@ -26,9 +26,9 @@ public class ProvidableClass { public void provide( Object instance, ServiceProvider provider ) throws ServiceProviderException, IllegalArgumentException, IllegalAccessException { - postConstructor.accept(instance); for ( final ProvidableField field : fields ) field.provide( instance, provider ); + postConstructor.accept(instance); } public static ProvidableClass wrap( QualifierExtractor extractor, Class targetClazz ) { From 84f077d4838c0383b945c6f7cff56d7a27699513 Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 23 Jul 2015 16:49:03 -0300 Subject: [PATCH 18/26] Removed annoying e.printStackTrace instruction --- trip-jsr-330/source/trip/jsr/cdi/CDILoaderOfClasses.java | 1 - 1 file changed, 1 deletion(-) diff --git a/trip-jsr-330/source/trip/jsr/cdi/CDILoaderOfClasses.java b/trip-jsr-330/source/trip/jsr/cdi/CDILoaderOfClasses.java index f35458f..07ed321 100644 --- a/trip-jsr-330/source/trip/jsr/cdi/CDILoaderOfClasses.java +++ b/trip-jsr-330/source/trip/jsr/cdi/CDILoaderOfClasses.java @@ -9,7 +9,6 @@ static Class loadClass( String className ){ try { return (Class)Class.forName(className); } catch (final ClassNotFoundException e) { - e.printStackTrace(); return null; } } From 9b1d757a81d3039963da85fcc8454172b4469213 Mon Sep 17 00:00:00 2001 From: "Miere L. Teixeira" Date: Thu, 23 Jul 2015 18:12:51 -0300 Subject: [PATCH 19/26] First draft of new 'single page documentation' --- README.md | 221 ++++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 198 insertions(+), 23 deletions(-) diff --git a/README.md b/README.md index 66d9c02..99036e0 100644 --- a/README.md +++ b/README.md @@ -1,29 +1,204 @@ # tRip - Low coupling module development tRip intent to be a practical and lightweight tool to provide modularity and low coupling on your source code. -### Main Features -tRip was designed to: -- Take care of Singleton's and Stateless' services for your -- Allow you to create modules and extensions to your software without change one LOC in the core implementation -- Fast warm-up: tRip already knows what to provide in compilation phase, there's no need to look into the entire class-path for provided classes -- Zero configuration: just let the [ServiceProvider](https://github.com/Skullabs/tRip/wiki/Getting-Started:-My-First-Modular-App) run the software for you -- Factory-based creation of services: you can take control of how a service is provided creating your own factory -- Manually provided data: you still can provide data manually to your software context. - -### Low footprint -tRip is basically two jars: -- trip-core ( 30kb ): which is needed to run your application -- trip-processor ( 28kb ): which is responsible by the auto discovery of Singleton's and Stateless' services on your modules. This dependency is needed only during compilation phase. - -### Lets get started? -Here is a little tour of tRip main features. -- [Configuring a project with tRip](https://github.com/Skullabs/tRip/wiki/Configuring-a-project-with-tRip) -- [My First Modular App](https://github.com/Skullabs/tRip/wiki/Getting-Started:-My-First-Modular-App) -- [Understanding ServiceProvider](https://github.com/Skullabs/tRip/wiki/Undertanding-ServiceProvider) -- [The Injection Process](https://github.com/Skullabs/tRip/wiki/The-Injection-Process) -- [Provinding Services](https://github.com/Skullabs/tRip/wiki/Providing-Services) - - [Automatically](https://github.com/Skullabs/tRip/wiki/Providing-Services:-Automatically-Way) - - [Manually](https://github.com/Skullabs/tRip/wiki/Providing-Services:-Manually-Way) +## Main Features +- Compile time discovery of Service Provider Interfaces (SPI) +- Optimized runtime injection of services (discovered SPIs) +- Orthogonal factory pattern (Similar to ```@Produces``` from JSR299) +- CDI (JSR 330) compliant API +- Very low memory footprint +- Lightweight dependency stack (1 JAR -> ~35kb) + +### 1. Configuring a Maven Project +Just include the following libraries to your pom.xml. +```xml + + + io.skullabs.trip + trip-core + ${version.trip} + + + + io.skullabs.trip + trip-processor + ${version.trip} + provided + +``` +If you intent to use CDI (JSR330) annotations, please include the following library too. +```xml + + io.skullabs.trip + trip-core + ${version.trip} + + + +``` + +### 2. Providing Services +Bellow some examples how you can make a class available to the _tRip Context_. +```java +import trip.spi.*; + +public interface Player {} + +@Singleton( exposedAs=Player.class ) +// will expose Hero as a Player +public class Hero implements Player {} + +@Stateless( exposedAs=Player.class ) +// will expose BadBoy as a Player, managed as Stateless +public class BadBoy implements Player {} + +@Singleton +public class Game {} +``` + +### 3. Injecting Provided Services +```java +import trip.spi.*; + +public interface Player {} +public interface Weapon {} + +@Singleton( exposedAs=Weapon.class ) +public class Granade implements Weapon {} + +@Singleton( exposedAs=Player.class ) +public class BadBoy implements Player { + + @Provided Weapon weapon; + +} +``` +You can inject concrete implementations into your services. It is not required to +expose services as interfaces (or abstract classes). +```java +// exposing itself as a service +@Singleton +public class Menu {} + +// injecting the concrete implementation +@Singleton +public class Game { + @Provided Menu menu; +} +``` + +#### 3.1 Injecting all implementations of a service +Sometimes it would be nice if was possible to retrieve all implementations of a specific +interface and have them injected into your controller. tRip provides this functionality through +the ```@ProvidedServices``` annotation as illustrated bellow. +```java +import trip.spi.*; + +public interface Player {} + +@Singleton( exposedAs=Player.class ) +// will expose Hero as a Player +public class Hero implements Player {} + +@Stateless( exposedAs=Player.class ) +// will expose BadBoy as a Player, managed as Stateless +public class BadBoy implements Player {} + +@Singleton +public class Game { + + @ProvidedServices( exposedAs=Player.class ) + Iterable allAvailablePlayers; + +} +``` + +#### 3.2 Disambiguation +Sometimes we have more than one dependency provided with some Interface. It is possible to +create a qualifier annotation (in a JSR330 way), to identify which implementation should +be injected. +```java +import trip.spi.*; + +public interface Weapon {} + +@Qualifier +@Retention( RUNTIME ) +public @interface HiDamage {} + +@HiDamage +@Singleton( exposedAs=Weapon.class ) +public class Granade implements Weapon {} + +@Singleton( exposedAs=Weapon.class ) +public class Gun implements Weapon {} + +@Singleton( exposedAs=Player.class ) +public class BadBoy implements Player { + + @HiDamage + @Provided + Weapon weapon; + +} + +// You can always inject a specific concrete implementation +@Singleton( exposedAs=Player.class ) +public class BadBoy implements Player { + + @Provided(exposedAs=Weapon.class) + Gun gun; + +} +``` + +### 4. Managing (injecting into) classes with tRip Context +You should have at least one managed class to have its dependencies injected. tRip have +a context implementation called ```trip.spi.ServiceProvide```. It controls all injectable data, +are capable to handle singletons and stateless services, and instantiate classes when it is +required as a dependency but it neither is a singleton nor a stateless implementation. + + +```java +import trip.spi.*; + +@Singleton +public class Game { /* ... */ } + +public class Main { + + public static void main( String[] args ){ + ServiceProvider provider = new DefaultServiceProvider(); + Game game = provider.load( Game.class ); + game.run(); + } +} +``` +The above example code shows how is possible to intialize the ```Game``` class +and have its dependencies injected. The dependencies' dependencies will be recursively injected. +In this case, will be only instance of ```Game``` no matter how many times ```ServiceProvider.load``` +is called. If neither ```@Stateless``` nor ```@Singleton``` annotations is present, then a new instance +of Game will be created everytime ```ServiceProvider.load``` is called. + +It is also possible to inject data in an already created instance of any class. +```java + public static void main( String[] args ){ + ServiceProvider provider = new DefaultServiceProvider(); + Game game = new Game(); + provider.provideOn( game ); + game.run(); + } +``` + +To avoid side effects on your application, there should be only one instance of ServiceProvider on your +project. If you need access to current tRip Context you should inject it on your code. +```java +public class AClassThatUsesServiceProvider { + + @Provided ServiceProvider provider; + +} +``` ### License tRip is Apache 2.0 licensed. From bc987318dfb490a6a13d3ab04369d4b0cc82fe54 Mon Sep 17 00:00:00 2001 From: Miere Teixeira Date: Thu, 23 Jul 2015 18:38:48 -0300 Subject: [PATCH 20/26] Added stateless and singleton description --- README.md | 43 +++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 41 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 99036e0..c6f78f6 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,7 @@ tRip intent to be a practical and lightweight tool to provide modularity and low coupling on your source code. ## Main Features +- Designed to help developers to create modular micro-services in Java. - Compile time discovery of Service Provider Interfaces (SPI) - Optimized runtime injection of services (discovered SPIs) - Orthogonal factory pattern (Similar to ```@Produces``` from JSR299) @@ -157,8 +158,6 @@ You should have at least one managed class to have its dependencies injected. tR a context implementation called ```trip.spi.ServiceProvide```. It controls all injectable data, are capable to handle singletons and stateless services, and instantiate classes when it is required as a dependency but it neither is a singleton nor a stateless implementation. - - ```java import trip.spi.*; @@ -200,5 +199,45 @@ public class AClassThatUsesServiceProvider { } ``` +#### 4.1. Stateless Services +Stateless services, as it name suggests, is a services that does not have state. It means that it does not stores data between two or more executions of a public method. Its simple to be handled by developers, once it is thread-safe by default ( except if this service is manually shared between threads ). The bellow source code illustrates how it works. +```java +public class User {} + +public class DatabaseAccess {} + +@Stateless +public class UserPersistenceService { + + @Provided DatabaseAccess dao; + + public void persist( User user ){ + dao.newTransaction(); + dao.persist( user ); + dao.commit(); + } +} + +@Singleton +// exposed as REST endpoint by your favorite REST framework +public class MyRESTEndpoint { + + @Provided UserPersistenceService userService; + + @POST @Path( "/api/user" ) + public void persistUser( User user ){ + userService.persist( user ); + } +} +``` +At the above sample code, every time the method ```UserPersistenceService.persist``` is called, a new instance of ```UserPersistenceService``` will be created, all its dependencies will be injected again and then the method you called will be executed. It makes easier to handle transactions in a clean way, as the above sample states. + +As ```Stateless``` services have a short life cycle, you can use the JDK ```PostConstruct``` and ```PreDestroy``` annotations to be notified, respectively, when the service is constructed and destructed before every call to a public service's method. + +#### 4.2. Singleton Services +It is a managed service that appears once in the context. Every consumer will receive the same instance of this service. Is assigned to the developer the responsibility to keep the service consistent and, if is the case, thread-safe. No secret here. Singletons services are memory friendly, but requires a more sophisticated design to avoid problems in a multi-threaded environment like a web server. + +Opposed to stateless services, ```Singleton``` services have a long life cycle. Thus tRip only supports the JDK's ```PostConstruct``` annotation. It is called every time a singleton service is called. + ### License tRip is Apache 2.0 licensed. From 7c5103a3a337f5565dd6f7109bbfcd0548cf96f8 Mon Sep 17 00:00:00 2001 From: Miere Teixeira Date: Thu, 23 Jul 2015 18:51:20 -0300 Subject: [PATCH 21/26] Added producer method examples --- README.md | 32 +++++++++++++++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index c6f78f6..14450b0 100644 --- a/README.md +++ b/README.md @@ -114,7 +114,37 @@ public class Game { } ``` -#### 3.2 Disambiguation +#### 3.2. Producing data orthogonally +Is possible to produce data to be injected using a factory pattern. To achieve this, tRip provides the __producer method__ approach. A producer method generates an object that can then be injected. Typically, you use producer methods in the following situations: +- When you want to inject an object that is not itself a bean +- When the concrete type of the object to be injected may vary at runtime +- When the object requires some custom initialization that the bean constructor does not perform. + +```java +@Singleton +public class DatabaseAccess { + + @Provided DatabaseConfig dbConfig; + + public Connection getConnection(){ + // a method that uses dbConfig to retrieve a database connection + } +} + +@Singleton +public class DBConfigProducer { + + @Producer + public DatabaseConfig produceDbConfig(){ + // produce dbConfig for further usage + } +} +``` +The above example illustrates how is possible to produce a DatabaseConfig at runtime. Everytime a service request for a ```DatabaseConfig``` dependency, the ```DBConfigProducer.produceDbConfig``` method will be called. It means: +- it will be called once per Singleton instance +- it will be called every time a Stateless' service public method is called + +#### 3.3 Disambiguation Sometimes we have more than one dependency provided with some Interface. It is possible to create a qualifier annotation (in a JSR330 way), to identify which implementation should be injected. From edf25e7242614e0a37303a2541d7f0e2d919e3f6 Mon Sep 17 00:00:00 2001 From: "Miere L. Teixeira" Date: Fri, 24 Jul 2015 12:59:27 -0300 Subject: [PATCH 22/26] Update on README.md --- README.md | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/README.md b/README.md index 14450b0..be5d12d 100644 --- a/README.md +++ b/README.md @@ -269,5 +269,28 @@ It is a managed service that appears once in the context. Every consumer will re Opposed to stateless services, ```Singleton``` services have a long life cycle. Thus tRip only supports the JDK's ```PostConstruct``` annotation. It is called every time a singleton service is called. +### Using CDI (JSR-330) Annotations +The Java CDI API (JSR-330) was designed to be a simple and intuitive Inversion of Control API. It is based on annotations and behaves very similar to tRip native SPI API. Since tRip 2.x version, tRip offers a JSR-330 support and also the ```@javax.enterprise.inject.Produces```. Bellow a comparison table between each JSR-330 annotations and tRip JSR-330 annotations. + +|Functionality|tRip|CDI JSR-330| +|:-------------|:-------------:|:-------------:| +|Singleton services|trip.spi.Singleton|javax.inject.Singleton| +|Stateless services|trip.spi.Stateless|N/A| +|Inject a service into a field|trip.spi.Provided|javax.inject.Inject| +|Inject all services into a field|trip.spi.ProvidedServices|N/A| +|Producer/factory method pattern|trip.spi.Producer|javax.enterprise.inject.Produces (JSR-299)| + +#### A note about ```@javax.inject.Singleton``` annotated services +The default tRip's ```@Singleton``` annotation will expose only the interface(or abstract class) defined +on ```Singleton.exposedAs``` attribute, or the concrete class itself if no attribute is set at all. + +When you are using JSR-330's ```@Singleton``` annotation, is expected that the container should be able +to inject corretly a service (either by the concrete class or through the interface) automatically. tRip +JSR-330 respect this rule and correctly injects the needed service as defined by the JSR-330 spec. + +### Contributing +tRip is a open source software. It means any help, improvement or bug fixed are welcome. Even typos on +this documentation is a very welcome improvement. + ### License tRip is Apache 2.0 licensed. From 47f7fe4f1f0e0f3e0548bdcd4f76ffd81ebaad6b Mon Sep 17 00:00:00 2001 From: unknown Date: Fri, 24 Jul 2015 13:15:04 -0300 Subject: [PATCH 23/26] Added useful Javadoc to ServiceProvider interface --- .../source/trip/spi/ServiceProvider.java | 69 ++++++++++++++++++- 1 file changed, 68 insertions(+), 1 deletion(-) diff --git a/trip-core/source/trip/spi/ServiceProvider.java b/trip-core/source/trip/spi/ServiceProvider.java index 86ac526..677bb7f 100644 --- a/trip-core/source/trip/spi/ServiceProvider.java +++ b/trip-core/source/trip/spi/ServiceProvider.java @@ -3,28 +3,95 @@ import trip.spi.helpers.QualifierExtractor; import trip.spi.helpers.filter.Condition; +/** + * The main tRip Context. It manages singleton and stateless instances, + * inject data into beans and create new instance of classes that could + * benefits with the tRip's injection mechanism. + */ public interface ServiceProvider { + /** + * Load a service represented by the argument {@code interfaceClazz}. + * If no service was found, it will try to instantiate the class and + * inject data. + * + * @param interfaceClazz - the service interface(or class) representation + * @return - the loaded or created service. + */ T load(Class interfaceClazz); + /** + * Load a service represented by the argument {@code interfaceClazz}. + * If no service was found, it will try to instantiate the class and + * inject data. + * + * @param interfaceClazz - the service interface(or class) representation + * @param condition - a filter condition + * @return - the loaded or created service. + */ T load(Class interfaceClazz, Condition condition); T load(Class interfaceClazz, ProviderContext context); T load(Class interfaceClazz, Condition condition, ProviderContext context); + /** + * Load all services represented by the argument {@code interfaceClazz}. + * If no service was found, it will try to instantiate the class, + * inject data and return an {@link Iterable} with this instance. + * + * @param interfaceClazz - the service interface(or class) representation + * @param condition - a filter condition + * @return - all loaded or created services. + */ Iterable loadAll(Class interfaceClazz, Condition condition); + /** + * Load all services represented by the argument {@code interfaceClazz}. + * If no service was found, it will try to instantiate the class, + * inject data and return an {@link Iterable} with this instance. + * + * @param interfaceClazz - the service interface(or class) representation + * @return - all loaded or created services. + */ Iterable loadAll(Class interfaceClazz); + /** + * Defines a factory to be invoked every time a service represented with + * {@code interfaceClazz} is requested. + * + * @param interfaceClazz - the service interface(or class) representation + * @param provider - the producer implementation + */ void providerFor(Class interfaceClazz, ProducerFactory provider); + /** + * Defines a factory to be invoked every time a service represented with + * {@code interfaceClazz} is requested. + * + * @param interfaceClazz - the service interface(or class) representation + * @param object - the service implementation + */ void providerFor(Class interfaceClazz, T object); + /** + * Inject data into all objects present in {@code iterable}. + * + * @param iterable - the set of objects that will receive "injectable" services. + */ void provideOn(Iterable iterable); + /** + * Inject data into {@code object}. + * + * @param object - the objects that will receive "injectable" services. + */ void provideOn(Object object); + /** + * Returns the QualifierExtractor. + * + * @return the QualifierExtractor. + */ QualifierExtractor getQualifierExtractor(); - } \ No newline at end of file From 0d8c40faa1cc808988c676f0c3728fe4c4fdd743 Mon Sep 17 00:00:00 2001 From: unknown Date: Mon, 27 Jul 2015 08:48:16 -0300 Subject: [PATCH 24/26] Removed unneeded method from ServiceProvider interface --- trip-core/source/trip/spi/ServiceProvider.java | 8 -------- 1 file changed, 8 deletions(-) diff --git a/trip-core/source/trip/spi/ServiceProvider.java b/trip-core/source/trip/spi/ServiceProvider.java index 677bb7f..7b8f44e 100644 --- a/trip-core/source/trip/spi/ServiceProvider.java +++ b/trip-core/source/trip/spi/ServiceProvider.java @@ -1,6 +1,5 @@ package trip.spi; -import trip.spi.helpers.QualifierExtractor; import trip.spi.helpers.filter.Condition; /** @@ -87,11 +86,4 @@ public interface ServiceProvider { * @param object - the objects that will receive "injectable" services. */ void provideOn(Object object); - - /** - * Returns the QualifierExtractor. - * - * @return the QualifierExtractor. - */ - QualifierExtractor getQualifierExtractor(); } \ No newline at end of file From d08331f3721af92f7785779981323400ee1de5aa Mon Sep 17 00:00:00 2001 From: unknown Date: Mon, 27 Jul 2015 08:49:49 -0300 Subject: [PATCH 25/26] Version 2.0.0 --- pom.xml | 2 +- sample/pom.xml | 2 +- trip-core/pom.xml | 2 +- trip-integration-tests/pom.xml | 2 +- trip-jsr-330-integration-tests/pom.xml | 2 +- trip-jsr-330/pom.xml | 2 +- trip-processor/pom.xml | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/pom.xml b/pom.xml index 5e0b85b..5866f32 100644 --- a/pom.xml +++ b/pom.xml @@ -2,7 +2,7 @@ 4.0.0 io.skullabs.trip - 2.0.0-SNAPSHOT + 2.0.0 trip-parent pom diff --git a/sample/pom.xml b/sample/pom.xml index 6d66c8f..1f2b8b9 100644 --- a/sample/pom.xml +++ b/sample/pom.xml @@ -4,7 +4,7 @@ io.skullabs.trip - 2.0.0-SNAPSHOT + 2.0.0 trip-parent diff --git a/trip-core/pom.xml b/trip-core/pom.xml index 7291128..a66e3b9 100644 --- a/trip-core/pom.xml +++ b/trip-core/pom.xml @@ -4,7 +4,7 @@ io.skullabs.trip - 2.0.0-SNAPSHOT + 2.0.0 trip-parent diff --git a/trip-integration-tests/pom.xml b/trip-integration-tests/pom.xml index 08924b6..446328e 100644 --- a/trip-integration-tests/pom.xml +++ b/trip-integration-tests/pom.xml @@ -4,7 +4,7 @@ io.skullabs.trip - 2.0.0-SNAPSHOT + 2.0.0 trip-parent diff --git a/trip-jsr-330-integration-tests/pom.xml b/trip-jsr-330-integration-tests/pom.xml index f905fde..8ecf198 100644 --- a/trip-jsr-330-integration-tests/pom.xml +++ b/trip-jsr-330-integration-tests/pom.xml @@ -4,7 +4,7 @@ io.skullabs.trip - 2.0.0-SNAPSHOT + 2.0.0 trip-parent diff --git a/trip-jsr-330/pom.xml b/trip-jsr-330/pom.xml index fd78a6f..12a5d07 100644 --- a/trip-jsr-330/pom.xml +++ b/trip-jsr-330/pom.xml @@ -4,7 +4,7 @@ io.skullabs.trip - 2.0.0-SNAPSHOT + 2.0.0 trip-parent diff --git a/trip-processor/pom.xml b/trip-processor/pom.xml index 8cdfb83..bf20fe4 100644 --- a/trip-processor/pom.xml +++ b/trip-processor/pom.xml @@ -4,7 +4,7 @@ io.skullabs.trip - 2.0.0-SNAPSHOT + 2.0.0 trip-parent From a5a7f620b16f5e0fb5106abcb11f06203c922935 Mon Sep 17 00:00:00 2001 From: "Miere L. Teixeira" Date: Mon, 27 Jul 2015 09:07:35 -0300 Subject: [PATCH 26/26] Fixed some regressions after last commit --- trip-core/source/trip/spi/DefaultServiceProvider.java | 1 - trip-core/source/trip/spi/ProviderContext.java | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/trip-core/source/trip/spi/DefaultServiceProvider.java b/trip-core/source/trip/spi/DefaultServiceProvider.java index 4f1f8ad..cb5e2fa 100644 --- a/trip-core/source/trip/spi/DefaultServiceProvider.java +++ b/trip-core/source/trip/spi/DefaultServiceProvider.java @@ -200,7 +200,6 @@ public ProducerFactory getProviderFor( final Class interfaceClazz, fin return (ProducerFactory)this.producers.get( interfaceClazz, condition ); } - @Override public QualifierExtractor getQualifierExtractor() { return qualifierExtractor; } diff --git a/trip-core/source/trip/spi/ProviderContext.java b/trip-core/source/trip/spi/ProviderContext.java index 7f86531..fddfa8b 100644 --- a/trip-core/source/trip/spi/ProviderContext.java +++ b/trip-core/source/trip/spi/ProviderContext.java @@ -48,6 +48,7 @@ public interface ProviderContext { * {@code key} was found. * * @param key + * @param * @return */ T attribute( Class key );