// Path: services-core/src/main/java/io/plumery/core/AggregateRoot.java
// public abstract class AggregateRoot {
//     private static final Logger logger = LoggerFactory.getLogger(AggregateRoot.class);
// 
//     private static final String APPLY_METHOD_NAME = "apply";
//     private final List<Event> changes = new ArrayList<>();
// 
//     public ID id;
//     public int version;
// 
//     protected AggregateRoot() {
//         this(null);
//     }
// 
//     protected AggregateRoot(ID id) {
//         this.id = id;
//     }
// 
//     public void markChangesAsCommitted() {
//         changes.clear();
//     }
// 
//     public final Iterable<? extends Event> getUncommittedChanges() {
//         return changes;
//     }
// 
//     public final void loadFromHistory(Iterable<? extends Event> history) {
//         for (Event e : history) {
//             if(version < e.version) {
//                 version = e.version;
//             }
//             applyChange(e, false);
//         }
//     }
// 
//     protected void applyChange(Event event) {
//         applyChange(event, true);
//     }
// 
//     private void applyChange(Event event, boolean isNew) {
//         invokeApplyIfEntitySupports(event);
// 
//         if (isNew) {
//             changes.add(event);
//         }
//     }
// 
//     private void invokeApplyIfEntitySupports(Event event) {
//         Class<?> eventType = nonAnonymous(event.getClass());
//         try {
//             Method method = this.getClass().getDeclaredMethod(APPLY_METHOD_NAME, eventType);
//             method.setAccessible(true);
//             method.invoke(this, event);
//         } catch (SecurityException | IllegalAccessException | InvocationTargetException ex) {
//             throw new RuntimeException(ex);
//         } catch (NoSuchMethodException ex) {
//             logger.warn("Event {} not applicable to {}!", event, this);
//         }
//     }
// 
//     @SuppressWarnings("unchecked")
//     private static <T> Class<T> nonAnonymous(Class<T> clazz) {
//         return clazz.isAnonymousClass() ? (Class<T>) clazz.getSuperclass() : clazz;
//     }
// }
// 
// Path: services-core/src/main/java/io/plumery/core/Event.java
// public abstract class Event implements Action {
//     public int version;
//     public ID id;
// }
// 
// Path: services-core/src/main/java/io/plumery/core/ID.java
// public class ID implements Serializable {
//     private final String id;
// 
//     private ID(String id) {
//         this.id = id;
//     }
// 
//     public static ID fromObject(Object id) {
//         if (id instanceof String) {
//             return new ID((String) id);
//         } else if (id instanceof UUID) {
//             return new ID(id.toString());
//         } else {
//             new IllegalArgumentException("The id should be of either String or UUID type");
//         }
// 
//         return null;
//     }
// 
//     public String toString() {
//         return id;
//     }
// }
// 
// Path: services-core/src/main/java/io/plumery/core/infrastructure/EventStore.java
// public interface EventStore {
//     void saveEvents(String streamName, String aggregateId, Iterable<? extends Event> events, int expectedVersion);
//     Iterable<? extends Event> getEventsForAggregate(Class<? extends AggregateRoot> aggregate, String aggregateId);
// }
// 
// Path: services-core/src/main/java/io/plumery/core/infrastructure/Repository.java
// public interface Repository<T extends AggregateRoot> {
//     public void save(T message, int version);
//     public T getById(ID id);
// }
// 
// Path: services-infrastructure-eventstore/src/main/java/io/plumery/eventstore/exception/AggregateNotFoundException.java
// public class AggregateNotFoundException extends RuntimeException {
//     public AggregateNotFoundException(String name, ID id) {
//         super(name + "; Id=" + id.toString());
//     }
// }
// 
// Path: services-infrastructure-eventstore/src/main/java/io/plumery/eventstore/utils/ReflectionHelper.java
// public static Class<? extends AggregateRoot> getParameterizedClass(Class clazz) {
//     ParameterizedType pt = (ParameterizedType) clazz.getGenericSuperclass();
//     @SuppressWarnings("unchecked")
//     Class<? extends AggregateRoot> parametrizedClazz = (Class<? extends AggregateRoot>) pt.getActualTypeArguments()[0];
// 
//     return parametrizedClazz;
// }
// 
// Path: services-infrastructure-eventstore/src/main/java/io/plumery/eventstore/utils/ReflectionHelper.java
// public static <T> T instantiate(final Class<T> clazz) {
//     try {
//         return AccessController.doPrivileged(new PrivilegedExceptionAction<T>() {
//             @Override
//             public T run() throws Exception {
//                 Constructor<T> constructor = clazz.getDeclaredConstructor();
//                 constructor.setAccessible(true);
//                 return constructor.newInstance();
//             }
//         });
//     } catch (PrivilegedActionException e) {
//         if (e.getCause() instanceof NoSuchMethodException) {
//             throw new RuntimeException(clazz + " must have a default constructor");
//         }
//         throw new RuntimeException(e);
//     }
// }


import com.google.common.base.Preconditions;
import com.google.common.collect.Iterables;
import io.plumery.core.AggregateRoot;
import io.plumery.core.Event;
import io.plumery.core.ID;
import io.plumery.core.infrastructure.EventStore;
import io.plumery.core.infrastructure.Repository;
import io.plumery.eventstore.exception.AggregateNotFoundException;
import static io.plumery.eventstore.utils.ReflectionHelper.getParameterizedClass;
import static io.plumery.eventstore.utils.ReflectionHelper.instantiate;
package io.plumery.eventstore;



/**
 * Generic Event Store aware Repository implementation
 */
public abstract class EventStoreAwareRepository<T extends AggregateRoot> implements Repository<T> {
    protected EventStore store;

// Path: services-core/src/main/java/io/plumery/core/AggregateRoot.java
// public abstract class AggregateRoot {
//     private static final Logger logger = LoggerFactory.getLogger(AggregateRoot.class);
// 
//     private static final String APPLY_METHOD_NAME = "apply";
//     private final List<Event> changes = new ArrayList<>();
// 
//     public ID id;
//     public int version;
// 
//     protected AggregateRoot() {
//         this(null);
//     }
// 
//     protected AggregateRoot(ID id) {
//         this.id = id;
//     }
// 
//     public void markChangesAsCommitted() {
//         changes.clear();
//     }
// 
//     public final Iterable<? extends Event> getUncommittedChanges() {
//         return changes;
//     }
// 
//     public final void loadFromHistory(Iterable<? extends Event> history) {
//         for (Event e : history) {
//             if(version < e.version) {
//                 version = e.version;
//             }
//             applyChange(e, false);
//         }
//     }
// 
//     protected void applyChange(Event event) {
//         applyChange(event, true);
//     }
// 
//     private void applyChange(Event event, boolean isNew) {
//         invokeApplyIfEntitySupports(event);
// 
//         if (isNew) {
//             changes.add(event);
//         }
//     }
// 
//     private void invokeApplyIfEntitySupports(Event event) {
//         Class<?> eventType = nonAnonymous(event.getClass());
//         try {
//             Method method = this.getClass().getDeclaredMethod(APPLY_METHOD_NAME, eventType);
//             method.setAccessible(true);
//             method.invoke(this, event);
//         } catch (SecurityException | IllegalAccessException | InvocationTargetException ex) {
//             throw new RuntimeException(ex);
//         } catch (NoSuchMethodException ex) {
//             logger.warn("Event {} not applicable to {}!", event, this);
//         }
//     }
// 
//     @SuppressWarnings("unchecked")
//     private static <T> Class<T> nonAnonymous(Class<T> clazz) {
//         return clazz.isAnonymousClass() ? (Class<T>) clazz.getSuperclass() : clazz;
//     }
// }
// 
// Path: services-core/src/main/java/io/plumery/core/Event.java
// public abstract class Event implements Action {
//     public int version;
//     public ID id;
// }
// 
// Path: services-core/src/main/java/io/plumery/core/ID.java
// public class ID implements Serializable {
//     private final String id;
// 
//     private ID(String id) {
//         this.id = id;
//     }
// 
//     public static ID fromObject(Object id) {
//         if (id instanceof String) {
//             return new ID((String) id);
//         } else if (id instanceof UUID) {
//             return new ID(id.toString());
//         } else {
//             new IllegalArgumentException("The id should be of either String or UUID type");
//         }
// 
//         return null;
//     }
// 
//     public String toString() {
//         return id;
//     }
// }
// 
// Path: services-core/src/main/java/io/plumery/core/infrastructure/EventStore.java
// public interface EventStore {
//     void saveEvents(String streamName, String aggregateId, Iterable<? extends Event> events, int expectedVersion);
//     Iterable<? extends Event> getEventsForAggregate(Class<? extends AggregateRoot> aggregate, String aggregateId);
// }
// 
// Path: services-core/src/main/java/io/plumery/core/infrastructure/Repository.java
// public interface Repository<T extends AggregateRoot> {
//     public void save(T message, int version);
//     public T getById(ID id);
// }
// 
// Path: services-infrastructure-eventstore/src/main/java/io/plumery/eventstore/exception/AggregateNotFoundException.java
// public class AggregateNotFoundException extends RuntimeException {
//     public AggregateNotFoundException(String name, ID id) {
//         super(name + "; Id=" + id.toString());
//     }
// }
// 
// Path: services-infrastructure-eventstore/src/main/java/io/plumery/eventstore/utils/ReflectionHelper.java
// public static Class<? extends AggregateRoot> getParameterizedClass(Class clazz) {
//     ParameterizedType pt = (ParameterizedType) clazz.getGenericSuperclass();
//     @SuppressWarnings("unchecked")
//     Class<? extends AggregateRoot> parametrizedClazz = (Class<? extends AggregateRoot>) pt.getActualTypeArguments()[0];
// 
//     return parametrizedClazz;
// }
// 
// Path: services-infrastructure-eventstore/src/main/java/io/plumery/eventstore/utils/ReflectionHelper.java
// public static <T> T instantiate(final Class<T> clazz) {
//     try {
//         return AccessController.doPrivileged(new PrivilegedExceptionAction<T>() {
//             @Override
//             public T run() throws Exception {
//                 Constructor<T> constructor = clazz.getDeclaredConstructor();
//                 constructor.setAccessible(true);
//                 return constructor.newInstance();
//             }
//         });
//     } catch (PrivilegedActionException e) {
//         if (e.getCause() instanceof NoSuchMethodException) {
//             throw new RuntimeException(clazz + " must have a default constructor");
//         }
//         throw new RuntimeException(e);
//     }
// }


// Path: services-infrastructure-eventstore/src/main/java/io/plumery/eventstore/EventStoreAwareRepository.java
import com.google.common.base.Preconditions;
import com.google.common.collect.Iterables;
import io.plumery.core.AggregateRoot;
import io.plumery.core.Event;
import io.plumery.core.ID;
import io.plumery.core.infrastructure.EventStore;
import io.plumery.core.infrastructure.Repository;
import io.plumery.eventstore.exception.AggregateNotFoundException;
import static io.plumery.eventstore.utils.ReflectionHelper.getParameterizedClass;
import static io.plumery.eventstore.utils.ReflectionHelper.instantiate;

package io.plumery.eventstore;



/**
 * Generic Event Store aware Repository implementation
 */
public abstract class EventStoreAwareRepository<T extends AggregateRoot> implements Repository<T> {
    protected EventStore store;

    @Override