├── .travis.yml ├── ch.bfh.swos.bookapp.jpa ├── src │ ├── main │ │ ├── resources │ │ │ ├── META-INF │ │ │ │ ├── MANIFEST.MF │ │ │ │ └── persistence.xml │ │ │ └── jpaContext.xml │ │ └── java │ │ │ └── ch │ │ │ └── bfh │ │ │ └── swos │ │ │ └── bookapp │ │ │ └── jpa │ │ │ ├── repository │ │ │ ├── BookRepository.java │ │ │ ├── AuthorRepository.java │ │ │ └── impl │ │ │ │ ├── JPABookRepository.java │ │ │ │ └── JPAAuthorRepository.java │ │ │ └── model │ │ │ ├── Book.java │ │ │ └── Author.java │ └── test │ │ └── java │ │ └── ch │ │ └── bfh │ │ └── swos │ │ └── bookapp │ │ └── jpa │ │ └── model │ │ ├── DeleteIT.java │ │ ├── ReadIT.java │ │ ├── CreateIT.java │ │ └── UpdateIT.java ├── pom.xml └── ch.bfh.swos.bookapp.jpa.iml ├── ch.bfh.swos.bookapp.vertx ├── src │ └── main │ │ ├── webapp │ │ ├── META-INF │ │ │ └── MANIFEST.MF │ │ ├── resources │ │ │ ├── img │ │ │ │ ├── books.png │ │ │ │ ├── glyphicons-halflings.png │ │ │ │ └── glyphicons-halflings-white.png │ │ │ ├── css │ │ │ │ ├── angular-ui.min.css │ │ │ │ └── jquery-ui.min.css │ │ │ └── js │ │ │ │ ├── application.js │ │ │ │ ├── lib │ │ │ │ ├── jquery.bootstrap-growl.min.js │ │ │ │ ├── vertxbus.min.js │ │ │ │ ├── angular-resource.min.js │ │ │ │ ├── angular-ui.min.js │ │ │ │ └── bootstrap.min.js │ │ │ │ ├── directives.js │ │ │ │ ├── services.js │ │ │ │ └── controllers.js │ │ ├── WEB-INF │ │ │ ├── web.xml │ │ │ └── rest-servlet.xml │ │ ├── pages │ │ │ ├── authors.html │ │ │ └── books.html │ │ └── index.html │ │ ├── resources │ │ └── log4j.xml │ │ └── java │ │ └── ch │ │ └── bfh │ │ └── swos │ │ └── bookapp │ │ └── vertx │ │ ├── controller │ │ ├── BookController.java │ │ ├── AuthorController.java │ │ └── ReplayController.java │ │ ├── VertXBean.java │ │ ├── listener │ │ ├── AuthorEventListener.java │ │ └── BookEventListener.java │ │ └── handler │ │ ├── AuthorCommandHandler.java │ │ └── BookCommandHandler.java ├── pom.xml └── ch.bfh.swos.bookapp.vertx.iml ├── ch.bfh.swos.bookapp.query ├── src │ └── main │ │ ├── java │ │ └── ch │ │ │ └── bfh │ │ │ └── swos │ │ │ └── bookapp │ │ │ └── query │ │ │ ├── repository │ │ │ ├── BookRepository.java │ │ │ ├── AuthorRepository.java │ │ │ └── impl │ │ │ │ ├── JdbcAuthorRepository.java │ │ │ │ └── JdbcBookRepository.java │ │ │ └── dto │ │ │ ├── AuthorDTO.java │ │ │ └── BookDTO.java │ │ └── resources │ │ └── queryContext.xml └── pom.xml ├── ch.bfh.swos.bookapp.cqrs ├── src │ ├── main │ │ ├── java │ │ │ └── ch │ │ │ │ └── bfh │ │ │ │ └── swos │ │ │ │ └── bookapp │ │ │ │ └── cqrs │ │ │ │ ├── book │ │ │ │ ├── domain │ │ │ │ │ ├── event │ │ │ │ │ │ ├── BookRemovedEvent.java │ │ │ │ │ │ ├── BookTitleChangedEvent.java │ │ │ │ │ │ └── BookAddedEvent.java │ │ │ │ │ └── Book.java │ │ │ │ ├── application │ │ │ │ │ ├── command │ │ │ │ │ │ ├── RemoveBookCommand.java │ │ │ │ │ │ ├── ChangeBookTitleCommand.java │ │ │ │ │ │ └── AddBookCommand.java │ │ │ │ │ └── handler │ │ │ │ │ │ └── BookCommandHandler.java │ │ │ │ └── query │ │ │ │ │ └── BookJPAEventListener.java │ │ │ │ ├── author │ │ │ │ ├── domain │ │ │ │ │ ├── event │ │ │ │ │ │ ├── AuthorRemovedEvent.java │ │ │ │ │ │ └── AuthorAddedEvent.java │ │ │ │ │ └── Author.java │ │ │ │ ├── application │ │ │ │ │ ├── command │ │ │ │ │ │ ├── RemoveAuthorCommand.java │ │ │ │ │ │ └── AddAuthorCommand.java │ │ │ │ │ └── handler │ │ │ │ │ │ └── AuthorCommandHandler.java │ │ │ │ └── query │ │ │ │ │ └── AuthorJPAEventListener.java │ │ │ │ └── common │ │ │ │ └── EventStorePlayer.java │ │ └── resources │ │ │ └── cqrsContext.xml │ └── test │ │ └── java │ │ └── ch │ │ └── bfh │ │ └── swos │ │ └── bookapp │ │ └── cqrs │ │ ├── author │ │ └── AuthorEventTest.java │ │ └── book │ │ └── BookEventTest.java ├── pom.xml └── ch.bfh.swos.bookapp.cqrs.iml ├── README.md ├── pom.xml └── LICENSE.txt /.travis.yml: -------------------------------------------------------------------------------- 1 | language: java 2 | -------------------------------------------------------------------------------- /ch.bfh.swos.bookapp.jpa/src/main/resources/META-INF/MANIFEST.MF: -------------------------------------------------------------------------------- 1 | Manifest-Version: 1.0 2 | Class-Path: 3 | 4 | -------------------------------------------------------------------------------- /ch.bfh.swos.bookapp.vertx/src/main/webapp/META-INF/MANIFEST.MF: -------------------------------------------------------------------------------- 1 | Manifest-Version: 1.0 2 | Class-Path: 3 | 4 | -------------------------------------------------------------------------------- /ch.bfh.swos.bookapp.vertx/src/main/webapp/resources/img/books.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rvillars/bookapp-cqrs/HEAD/ch.bfh.swos.bookapp.vertx/src/main/webapp/resources/img/books.png -------------------------------------------------------------------------------- /ch.bfh.swos.bookapp.vertx/src/main/webapp/resources/img/glyphicons-halflings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rvillars/bookapp-cqrs/HEAD/ch.bfh.swos.bookapp.vertx/src/main/webapp/resources/img/glyphicons-halflings.png -------------------------------------------------------------------------------- /ch.bfh.swos.bookapp.vertx/src/main/webapp/resources/img/glyphicons-halflings-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rvillars/bookapp-cqrs/HEAD/ch.bfh.swos.bookapp.vertx/src/main/webapp/resources/img/glyphicons-halflings-white.png -------------------------------------------------------------------------------- /ch.bfh.swos.bookapp.query/src/main/java/ch/bfh/swos/bookapp/query/repository/BookRepository.java: -------------------------------------------------------------------------------- 1 | package ch.bfh.swos.bookapp.query.repository; 2 | 3 | import ch.bfh.swos.bookapp.query.dto.BookDTO; 4 | 5 | import java.util.Collection; 6 | 7 | public interface BookRepository { 8 | 9 | public Collection list(); 10 | 11 | public void deleteAll(); 12 | } 13 | -------------------------------------------------------------------------------- /ch.bfh.swos.bookapp.query/src/main/java/ch/bfh/swos/bookapp/query/repository/AuthorRepository.java: -------------------------------------------------------------------------------- 1 | package ch.bfh.swos.bookapp.query.repository; 2 | 3 | import ch.bfh.swos.bookapp.query.dto.AuthorDTO; 4 | 5 | import java.util.Collection; 6 | 7 | public interface AuthorRepository { 8 | 9 | public Collection list(); 10 | 11 | public void deleteAll(); 12 | 13 | } 14 | -------------------------------------------------------------------------------- /ch.bfh.swos.bookapp.vertx/src/main/webapp/resources/css/angular-ui.min.css: -------------------------------------------------------------------------------- 1 | .ui-resetwrap{position:relative;display:inline-block}.ui-reset{position:absolute;top:0;right:0;z-index:2;display:none;height:100%;cursor:pointer}.ui-resetwrap:hover .ui-reset{display:block}.ui-currency-pos{color:green}.ui-currency-neg{color:red}.ui-currency-zero{color:blue}.ui-currency-pos.ui-bignum,.ui-currency-neg.ui-smallnum{font-size:110%}.ui-match{background:yellow} -------------------------------------------------------------------------------- /ch.bfh.swos.bookapp.vertx/src/main/webapp/resources/js/application.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var bookapp = angular.module('bookapp', ['controllers', 'services','directives']); 4 | 5 | bookapp.config(function ($routeProvider) { 6 | $routeProvider. 7 | when('/', {templateUrl: 'pages/books.html', activeMenu: 'books'}). 8 | when('/books', {templateUrl: 'pages/books.html', activeMenu: 'books'}). 9 | when('/authors', {templateUrl: 'pages/authors.html', activeMenu: 'authors'}). 10 | otherwise({redirectTo: '/'}); 11 | }); 12 | -------------------------------------------------------------------------------- /ch.bfh.swos.bookapp.jpa/src/main/java/ch/bfh/swos/bookapp/jpa/repository/BookRepository.java: -------------------------------------------------------------------------------- 1 | package ch.bfh.swos.bookapp.jpa.repository; 2 | 3 | import ch.bfh.swos.bookapp.jpa.model.Book; 4 | 5 | import java.util.Collection; 6 | 7 | public interface BookRepository { 8 | 9 | public Book create(Book book); 10 | 11 | public Book read(Long id); 12 | 13 | public Collection list(); 14 | 15 | public Book update(Book book); 16 | 17 | public void delete(Book book); 18 | 19 | public void deleteAll(); 20 | 21 | public Book readByBookId(String bookId); 22 | 23 | } 24 | -------------------------------------------------------------------------------- /ch.bfh.swos.bookapp.jpa/src/main/java/ch/bfh/swos/bookapp/jpa/repository/AuthorRepository.java: -------------------------------------------------------------------------------- 1 | package ch.bfh.swos.bookapp.jpa.repository; 2 | 3 | import ch.bfh.swos.bookapp.jpa.model.Author; 4 | 5 | import java.util.Collection; 6 | 7 | public interface AuthorRepository { 8 | 9 | public Author create(Author author); 10 | 11 | public Author read(long id); 12 | 13 | public Collection list(); 14 | 15 | public Author update(Author author); 16 | 17 | public void delete(Author author); 18 | 19 | public void deleteAll(); 20 | 21 | public Author readByAuthorId(String authorId); 22 | 23 | } 24 | -------------------------------------------------------------------------------- /ch.bfh.swos.bookapp.vertx/src/main/resources/log4j.xml: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /ch.bfh.swos.bookapp.vertx/src/main/webapp/WEB-INF/web.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | rest 8 | org.springframework.web.servlet.DispatcherServlet 9 | 1 10 | 11 | 12 | 13 | rest 14 | /rest/* 15 | 16 | 17 | -------------------------------------------------------------------------------- /ch.bfh.swos.bookapp.cqrs/src/main/java/ch/bfh/swos/bookapp/cqrs/book/domain/event/BookRemovedEvent.java: -------------------------------------------------------------------------------- 1 | package ch.bfh.swos.bookapp.cqrs.book.domain.event; 2 | 3 | import org.axonframework.commandhandling.annotation.TargetAggregateIdentifier; 4 | 5 | /** 6 | * Created with IntelliJ IDEA. 7 | * User: rovi 8 | * Date: 13.08.13 9 | * Time: 09:46 10 | * To change this template use File | Settings | File Templates. 11 | */ 12 | public class BookRemovedEvent { 13 | 14 | @TargetAggregateIdentifier 15 | private final String bookId; 16 | 17 | public BookRemovedEvent(String bookId) { 18 | this.bookId = bookId; 19 | } 20 | 21 | public String getBookId() { 22 | return bookId; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /ch.bfh.swos.bookapp.cqrs/src/main/java/ch/bfh/swos/bookapp/cqrs/book/application/command/RemoveBookCommand.java: -------------------------------------------------------------------------------- 1 | package ch.bfh.swos.bookapp.cqrs.book.application.command; 2 | 3 | import org.axonframework.commandhandling.annotation.TargetAggregateIdentifier; 4 | 5 | /** 6 | * Created with IntelliJ IDEA. 7 | * User: rovi 8 | * Date: 13.08.13 9 | * Time: 09:46 10 | * To change this template use File | Settings | File Templates. 11 | */ 12 | public class RemoveBookCommand { 13 | 14 | @TargetAggregateIdentifier 15 | private final String bookId; 16 | 17 | public RemoveBookCommand(String bookId) { 18 | this.bookId = bookId; 19 | } 20 | 21 | public String getBookId() { 22 | return bookId; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /ch.bfh.swos.bookapp.cqrs/src/main/java/ch/bfh/swos/bookapp/cqrs/author/domain/event/AuthorRemovedEvent.java: -------------------------------------------------------------------------------- 1 | package ch.bfh.swos.bookapp.cqrs.author.domain.event; 2 | 3 | import org.axonframework.commandhandling.annotation.TargetAggregateIdentifier; 4 | 5 | /** 6 | * Created with IntelliJ IDEA. 7 | * User: rovi 8 | * Date: 13.08.13 9 | * Time: 09:46 10 | * To change this template use File | Settings | File Templates. 11 | */ 12 | public class AuthorRemovedEvent { 13 | 14 | @TargetAggregateIdentifier 15 | private final String authorId; 16 | 17 | public AuthorRemovedEvent(String authorId) { 18 | this.authorId = authorId; 19 | } 20 | 21 | public String getAuthorId() { 22 | return authorId; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /ch.bfh.swos.bookapp.cqrs/src/main/java/ch/bfh/swos/bookapp/cqrs/author/application/command/RemoveAuthorCommand.java: -------------------------------------------------------------------------------- 1 | package ch.bfh.swos.bookapp.cqrs.author.application.command; 2 | 3 | import org.axonframework.commandhandling.annotation.TargetAggregateIdentifier; 4 | 5 | /** 6 | * Created with IntelliJ IDEA. 7 | * User: rovi 8 | * Date: 13.08.13 9 | * Time: 09:46 10 | * To change this template use File | Settings | File Templates. 11 | */ 12 | public class RemoveAuthorCommand { 13 | 14 | @TargetAggregateIdentifier 15 | private final String authorId; 16 | 17 | public RemoveAuthorCommand(String authorId) { 18 | this.authorId = authorId; 19 | } 20 | 21 | public String getAuthorId() { 22 | return authorId; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /ch.bfh.swos.bookapp.cqrs/src/main/java/ch/bfh/swos/bookapp/cqrs/book/domain/event/BookTitleChangedEvent.java: -------------------------------------------------------------------------------- 1 | package ch.bfh.swos.bookapp.cqrs.book.domain.event; 2 | 3 | import org.axonframework.commandhandling.annotation.TargetAggregateIdentifier; 4 | 5 | /** 6 | * Created with IntelliJ IDEA. 7 | * User: rovi 8 | * Date: 13.08.13 9 | * Time: 09:46 10 | * To change this template use File | Settings | File Templates. 11 | */ 12 | public class BookTitleChangedEvent { 13 | 14 | @TargetAggregateIdentifier 15 | private final String bookId; 16 | private final String newTitle; 17 | 18 | 19 | public BookTitleChangedEvent(String bookId, String newTitle) { 20 | this.bookId = bookId; 21 | this.newTitle = newTitle; 22 | } 23 | 24 | public String getBookId() { 25 | return bookId; 26 | } 27 | 28 | public String getNewTitle() { 29 | return newTitle; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /ch.bfh.swos.bookapp.cqrs/src/main/java/ch/bfh/swos/bookapp/cqrs/book/application/command/ChangeBookTitleCommand.java: -------------------------------------------------------------------------------- 1 | package ch.bfh.swos.bookapp.cqrs.book.application.command; 2 | 3 | import org.axonframework.commandhandling.annotation.TargetAggregateIdentifier; 4 | 5 | /** 6 | * Created with IntelliJ IDEA. 7 | * User: rovi 8 | * Date: 13.08.13 9 | * Time: 09:46 10 | * To change this template use File | Settings | File Templates. 11 | */ 12 | public class ChangeBookTitleCommand { 13 | 14 | @TargetAggregateIdentifier 15 | private final String bookId; 16 | private final String newTitle; 17 | 18 | public ChangeBookTitleCommand(String bookId, String newTitle) { 19 | this.bookId = bookId; 20 | this.newTitle = newTitle; 21 | } 22 | 23 | public String getBookId() { 24 | return bookId; 25 | } 26 | 27 | public String getNewTitle() { 28 | return newTitle; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /ch.bfh.swos.bookapp.jpa/src/test/java/ch/bfh/swos/bookapp/jpa/model/DeleteIT.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | */ 4 | package ch.bfh.swos.bookapp.jpa.model; 5 | 6 | import org.junit.Test; 7 | 8 | import javax.persistence.EntityManager; 9 | import javax.persistence.Persistence; 10 | import javax.persistence.Query; 11 | import java.util.List; 12 | 13 | /** 14 | * @author rovi 15 | * 16 | */ 17 | public class DeleteIT { 18 | 19 | @Test 20 | public void test() { 21 | 22 | EntityManager em = Persistence.createEntityManagerFactory( 23 | "ch.bfh.swos.bookapp.book.domain").createEntityManager(); 24 | 25 | Query q = em.createQuery("select a from Author a"); 26 | @SuppressWarnings("unchecked") 27 | List foundAuthors = q.getResultList(); 28 | Author firstAuthor = foundAuthors.get(0); 29 | 30 | // Write access needs a transaction 31 | em.getTransaction().begin(); 32 | em.remove(firstAuthor); 33 | em.getTransaction().commit(); 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /ch.bfh.swos.bookapp.vertx/src/main/java/ch/bfh/swos/bookapp/vertx/controller/BookController.java: -------------------------------------------------------------------------------- 1 | package ch.bfh.swos.bookapp.vertx.controller; 2 | 3 | import ch.bfh.swos.bookapp.query.dto.BookDTO; 4 | import ch.bfh.swos.bookapp.query.repository.BookRepository; 5 | import org.springframework.stereotype.Controller; 6 | import org.springframework.web.bind.annotation.RequestMapping; 7 | import org.springframework.web.bind.annotation.RequestMethod; 8 | import org.springframework.web.bind.annotation.ResponseBody; 9 | 10 | import javax.inject.Inject; 11 | import java.util.Collection; 12 | 13 | @Controller 14 | @RequestMapping("/books") 15 | public class BookController { 16 | 17 | @Inject 18 | private BookRepository bookRepository; 19 | 20 | /** 21 | * ReadAll 22 | */ 23 | @RequestMapping(method = RequestMethod.GET) 24 | @ResponseBody 25 | public Collection list() { 26 | System.out.println("Collection of Book requested"); 27 | return bookRepository.list(); 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /ch.bfh.swos.bookapp.vertx/src/main/java/ch/bfh/swos/bookapp/vertx/controller/AuthorController.java: -------------------------------------------------------------------------------- 1 | package ch.bfh.swos.bookapp.vertx.controller; 2 | 3 | import ch.bfh.swos.bookapp.query.dto.AuthorDTO; 4 | import ch.bfh.swos.bookapp.query.repository.AuthorRepository; 5 | import org.springframework.stereotype.Controller; 6 | import org.springframework.web.bind.annotation.RequestMapping; 7 | import org.springframework.web.bind.annotation.RequestMethod; 8 | import org.springframework.web.bind.annotation.ResponseBody; 9 | 10 | import javax.inject.Inject; 11 | import java.util.Collection; 12 | 13 | @Controller 14 | @RequestMapping("/authors") 15 | public class AuthorController { 16 | 17 | @Inject 18 | private AuthorRepository authorRepository; 19 | 20 | /** 21 | * ReadAll 22 | */ 23 | @RequestMapping(method = RequestMethod.GET) 24 | @ResponseBody 25 | public Collection list() { 26 | System.out.println("Collection of Author requested"); 27 | return authorRepository.list(); 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /ch.bfh.swos.bookapp.vertx/src/main/webapp/WEB-INF/rest-servlet.xml: -------------------------------------------------------------------------------- 1 | 2 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | bookapp-cqrs [![Build Status](https://travis-ci.org/rvillars/bookapp-cqrs.png?branch=master)](https://travis-ci.org/rvillars/bookapp-cqrs) 2 | ============ 3 | This is a small example bookstore application with a basic CQRS/DDD driven approach. 4 | It uses Axon Framework for server-side CQRS and an embedded Vert.X eventbus to propagate events directly to the client. 5 | The client uses AngularJS to handle the events. 6 | 7 | The example consists currently of just two aggregates so it isn't actually a good example for Domain Driven Development. 8 | But it shows how to implement a Command/Event driven application interface with Axon and a way to extend this approach to 9 | web clients without using the very non-eventdriven REST API. 10 | 11 | For a short introduction in CQRS see [this Prezi](http://prezi.com/bbbqharguddg/?utm_campaign=share&utm_medium=copy&rc=ex0share): 12 | 13 | Technologies used: 14 | * Embedded Vert.X 15 | * Axon Framework 16 | * CQRS/DDD 17 | * AngularJS 18 | * jQueryUI 19 | * SpringMVC 20 | * maven 21 | * JPA/EclipseLink 22 | 23 | 24 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4.0.0 4 | ch.bfh.swos.bookapp 5 | ch.bfh.swos.bookapp.parent 6 | 0.0.1-SNAPSHOT 7 | pom 8 | Bookapp 9 | 10 | 11 | ch.bfh.swos.bookapp.jpa 12 | ch.bfh.swos.bookapp.cqrs 13 | ch.bfh.swos.bookapp.vertx 14 | ch.bfh.swos.bookapp.query 15 | 16 | 17 | 18 | 19 | 20 | org.apache.maven.plugins 21 | maven-compiler-plugin 22 | 3.1 23 | 24 | 1.7 25 | 1.7 26 | 27 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /ch.bfh.swos.bookapp.query/src/main/java/ch/bfh/swos/bookapp/query/dto/AuthorDTO.java: -------------------------------------------------------------------------------- 1 | package ch.bfh.swos.bookapp.query.dto; 2 | 3 | import java.io.Serializable; 4 | 5 | /** 6 | * Entity implementation class for Entity: Author 7 | * 8 | */ 9 | public class AuthorDTO implements Serializable { 10 | 11 | private String authorId; 12 | private String firstname; 13 | private String lastname; 14 | 15 | public String getAuthorId() { 16 | return authorId; 17 | } 18 | 19 | public void setAuthorId(String authorId) { 20 | this.authorId = authorId; 21 | } 22 | 23 | public String getFirstname() { 24 | return this.firstname; 25 | } 26 | 27 | public void setFirstname(String firstname) { 28 | this.firstname = firstname; 29 | } 30 | 31 | public String getLastname() { 32 | return this.lastname; 33 | } 34 | 35 | public void setLastname(String lastname) { 36 | this.lastname = lastname; 37 | } 38 | 39 | @Override 40 | public boolean equals(Object obj) { 41 | return this.authorId.equals(((AuthorDTO) obj).getAuthorId()); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /ch.bfh.swos.bookapp.cqrs/src/main/java/ch/bfh/swos/bookapp/cqrs/author/domain/event/AuthorAddedEvent.java: -------------------------------------------------------------------------------- 1 | package ch.bfh.swos.bookapp.cqrs.author.domain.event; 2 | 3 | import org.axonframework.commandhandling.annotation.TargetAggregateIdentifier; 4 | 5 | /** 6 | * Created with IntelliJ IDEA. 7 | * User: rovi 8 | * Date: 13.08.13 9 | * Time: 09:46 10 | * To change this template use File | Settings | File Templates. 11 | */ 12 | public class AuthorAddedEvent { 13 | 14 | @TargetAggregateIdentifier 15 | private final String authorId; 16 | private final String firstname; 17 | private final String lastname; 18 | 19 | 20 | public AuthorAddedEvent(String authorId, String firstname, String lastname) { 21 | this.authorId = authorId; 22 | this.firstname = firstname; 23 | this.lastname = lastname; 24 | } 25 | 26 | public String getAuthorId() { 27 | return authorId; 28 | } 29 | 30 | public String getFirstname() { 31 | return firstname; 32 | } 33 | 34 | public String getLastname() { 35 | return lastname; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /ch.bfh.swos.bookapp.jpa/src/test/java/ch/bfh/swos/bookapp/jpa/model/ReadIT.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | */ 4 | package ch.bfh.swos.bookapp.jpa.model; 5 | 6 | import org.junit.Assert; 7 | import org.junit.Test; 8 | 9 | import javax.persistence.EntityManager; 10 | import javax.persistence.Persistence; 11 | import javax.persistence.Query; 12 | import java.util.ArrayList; 13 | import java.util.List; 14 | 15 | /** 16 | * @author rovi 17 | * 18 | */ 19 | public class ReadIT { 20 | 21 | @Test 22 | public void test() { 23 | 24 | EntityManager em = Persistence.createEntityManagerFactory( 25 | "ch.bfh.swos.bookapp.book.domain").createEntityManager(); 26 | 27 | Query q = em.createQuery("select a from Author a"); 28 | @SuppressWarnings("unchecked") 29 | List foundAuthors = q.getResultList(); 30 | Author firstAuthor = foundAuthors.get(0); 31 | Assert.assertTrue(firstAuthor.getLastname().equals("Tolkien")); 32 | 33 | List foundBooks = new ArrayList(firstAuthor.getBooks()); 34 | Book firstBook = foundBooks.get(0); 35 | Assert.assertTrue(firstBook.getTitle().startsWith("Der Herr der Ringe")); 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /ch.bfh.swos.bookapp.cqrs/src/main/java/ch/bfh/swos/bookapp/cqrs/common/EventStorePlayer.java: -------------------------------------------------------------------------------- 1 | package ch.bfh.swos.bookapp.cqrs.common; 2 | 3 | import org.axonframework.domain.DomainEventMessage; 4 | import org.axonframework.eventhandling.EventBus; 5 | import org.axonframework.eventstore.EventVisitor; 6 | import org.axonframework.eventstore.jpa.JpaEventStore; 7 | import org.springframework.stereotype.Component; 8 | 9 | import javax.inject.Inject; 10 | import javax.inject.Named; 11 | 12 | /** 13 | * Created with IntelliJ IDEA. 14 | * User: rovi 15 | * Date: 20.09.13 16 | * Time: 09:09 17 | * To change this template use File | Settings | File Templates. 18 | */ 19 | @Component 20 | public class EventStorePlayer { 21 | 22 | @Inject 23 | @Named("eventStore") 24 | private JpaEventStore eventStore; 25 | 26 | @Inject 27 | @Named("eventBus") 28 | private EventBus eventBus; 29 | 30 | public void replay() { 31 | eventStore.visitEvents(new EventVisitor() { 32 | @Override 33 | public void doWithEvent(DomainEventMessage domainEvent) { 34 | eventBus.publish(domainEvent); 35 | } 36 | }); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /ch.bfh.swos.bookapp.query/src/main/java/ch/bfh/swos/bookapp/query/dto/BookDTO.java: -------------------------------------------------------------------------------- 1 | package ch.bfh.swos.bookapp.query.dto; 2 | 3 | import java.io.Serializable; 4 | import java.util.Date; 5 | 6 | /** 7 | * Entity implementation class for Entity: Book 8 | * 9 | */ 10 | public class BookDTO implements Serializable { 11 | 12 | private String bookId; 13 | private String title; 14 | private Date releaseDate; 15 | 16 | private AuthorDTO author; 17 | 18 | public BookDTO() { 19 | super(); 20 | } 21 | 22 | public String getBookId() { 23 | return bookId; 24 | } 25 | 26 | public void setBookId(String bookId) { 27 | this.bookId = bookId; 28 | } 29 | 30 | public String getTitle() { 31 | return this.title; 32 | } 33 | 34 | public void setTitle(String title) { 35 | this.title = title; 36 | } 37 | 38 | public Date getReleaseDate() { 39 | return this.releaseDate; 40 | } 41 | 42 | public void setReleaseDate(Date releaseDate) { 43 | this.releaseDate = releaseDate; 44 | } 45 | 46 | public AuthorDTO getAuthor() { 47 | return author; 48 | } 49 | 50 | public void setAuthor(AuthorDTO author) { 51 | this.author = author; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /ch.bfh.swos.bookapp.query/src/main/resources/queryContext.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /ch.bfh.swos.bookapp.vertx/src/main/webapp/resources/js/lib/jquery.bootstrap-growl.min.js: -------------------------------------------------------------------------------- 1 | (function(){var t;t=jQuery,t.bootstrapGrowl=function(e,s){var a,o,l;switch(s=t.extend({},t.bootstrapGrowl.default_options,s),a=t("
"),a.attr("class","bootstrap-growl alert"),s.type&&a.addClass("alert-"+s.type),s.allow_dismiss&&a.append('×'),a.append(e),s.top_offset&&(s.offset={from:"top",amount:s.top_offset}),l=s.offset.amount,t(".bootstrap-growl").each(function(){return l=Math.max(l,parseInt(t(this).css(s.offset.from))+t(this).outerHeight()+s.stackup_spacing)}),o={position:"body"===s.ele?"fixed":"absolute",margin:0,"z-index":"9999",display:"none"},o[s.offset.from]=l+"px",a.css(o),"auto"!==s.width&&a.css("width",s.width+"px"),t(s.ele).append(a),s.align){case"center":a.css({left:"50%","margin-left":"-"+a.outerWidth()/2+"px"});break;case"left":a.css("left","20px");break;default:a.css("right","20px")}return a.fadeIn(),s.delay>0&&a.delay(s.delay).fadeOut(function(){return t(this).alert("close")}),a},t.bootstrapGrowl.default_options={ele:"body",type:null,offset:{from:"top",amount:20},align:"right",width:250,delay:4e3,allow_dismiss:!0,stackup_spacing:10}}).call(this); 2 | -------------------------------------------------------------------------------- /ch.bfh.swos.bookapp.jpa/src/test/java/ch/bfh/swos/bookapp/jpa/model/CreateIT.java: -------------------------------------------------------------------------------- 1 | package ch.bfh.swos.bookapp.jpa.model; 2 | 3 | import org.junit.Test; 4 | 5 | import javax.persistence.EntityManager; 6 | import javax.persistence.Persistence; 7 | import java.util.Date; 8 | import java.util.HashSet; 9 | import java.util.Set; 10 | 11 | public class CreateIT { 12 | 13 | @Test 14 | public void test() { 15 | Author author = new Author(); 16 | author.setFirstname("J.R.R"); 17 | author.setLastname("Tolkien"); 18 | 19 | Book book = new Book(); 20 | book.setTitle("Der Herr der Ringe - Die Gefaehrten"); 21 | book.setReleaseDate(new Date()); 22 | 23 | Book book2 = new Book(); 24 | book2.setTitle("Der Herr der Ringe - Die zwei Tuerme"); 25 | book2.setReleaseDate(new Date()); 26 | 27 | Set books = new HashSet(); 28 | books.add(book); 29 | books.add(book2); 30 | author.setBooks(books); 31 | book.setAuthor(author); 32 | book2.setAuthor(author); 33 | 34 | EntityManager em = Persistence.createEntityManagerFactory( 35 | "ch.bfh.swos.bookapp.book.domain").createEntityManager(); 36 | 37 | em.getTransaction().begin(); 38 | em.persist(author); 39 | em.getTransaction().commit(); 40 | } 41 | 42 | } 43 | -------------------------------------------------------------------------------- /ch.bfh.swos.bookapp.cqrs/src/main/java/ch/bfh/swos/bookapp/cqrs/book/domain/event/BookAddedEvent.java: -------------------------------------------------------------------------------- 1 | package ch.bfh.swos.bookapp.cqrs.book.domain.event; 2 | 3 | import org.axonframework.commandhandling.annotation.TargetAggregateIdentifier; 4 | 5 | import java.util.Date; 6 | 7 | /** 8 | * Created with IntelliJ IDEA. 9 | * User: rovi 10 | * Date: 13.08.13 11 | * Time: 09:46 12 | * To change this template use File | Settings | File Templates. 13 | */ 14 | public class BookAddedEvent { 15 | 16 | @TargetAggregateIdentifier 17 | private final String bookId; 18 | private final String title; 19 | private final Date releaseDate; 20 | private final String authorId; 21 | 22 | 23 | public BookAddedEvent(String bookId, String title, Date releaseDate, String authorId) { 24 | this.bookId = bookId; 25 | this.title = title; 26 | this.releaseDate = releaseDate; 27 | this.authorId = authorId; 28 | } 29 | 30 | public String getBookId() { 31 | return bookId; 32 | } 33 | 34 | public String getTitle() { 35 | return title; 36 | } 37 | 38 | public Date getReleaseDate() { 39 | return releaseDate; 40 | } 41 | 42 | public String getAuthorId() { 43 | return authorId; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /ch.bfh.swos.bookapp.jpa/src/test/java/ch/bfh/swos/bookapp/jpa/model/UpdateIT.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | */ 4 | package ch.bfh.swos.bookapp.jpa.model; 5 | 6 | import org.junit.Assert; 7 | import org.junit.Test; 8 | 9 | import javax.persistence.EntityManager; 10 | import javax.persistence.Persistence; 11 | import javax.persistence.Query; 12 | import java.util.List; 13 | 14 | /** 15 | * @author rovi 16 | * 17 | */ 18 | public class UpdateIT { 19 | 20 | @Test 21 | public void test() { 22 | 23 | EntityManager em = Persistence.createEntityManagerFactory( 24 | "ch.bfh.swos.bookapp.book.domain").createEntityManager(); 25 | 26 | Query q = em.createQuery("select a from Author a"); 27 | @SuppressWarnings("unchecked") 28 | List foundAuthors = q.getResultList(); 29 | Author firstAuthor = foundAuthors.get(0); 30 | 31 | // Write access needs a transaction 32 | em.getTransaction().begin(); 33 | firstAuthor.setLastname("OtherName"); 34 | em.getTransaction().commit(); 35 | // Entity is persisted automatically after commit because it is managed 36 | // by jpa. 37 | 38 | @SuppressWarnings("unchecked") 39 | List updatedAuthors = q.getResultList(); 40 | Author updatedAuthor = updatedAuthors.get(0); 41 | Assert.assertTrue(updatedAuthor.getLastname().equals("OtherName")); 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /ch.bfh.swos.bookapp.jpa/src/main/resources/META-INF/persistence.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | ch.bfh.swos.bookapp.jpa.model.Author 5 | ch.bfh.swos.bookapp.jpa.model.Book 6 | 7 | org.axonframework.eventstore.jpa.DomainEventEntry 8 | org.axonframework.eventstore.jpa.SnapshotEventEntry 9 | 10 | true 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /ch.bfh.swos.bookapp.vertx/src/main/webapp/resources/js/directives.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var directives = angular.module('directives', ['ui.directives']); 4 | 5 | directives.directive('date', ['dateFilter', function (dateFilter) { 6 | return { 7 | require:'ngModel', 8 | link:function (scope, elm, attrs, ctrl) { 9 | 10 | var dateFormat = attrs['date'] || 'dd.MM.yyyy'; 11 | var minDate = Date.parse(attrs['min']) || 0; 12 | var maxDate = Date.parse(attrs['max']) || 9007199254740992; 13 | 14 | ctrl.$parsers.unshift(function (viewValue) { 15 | var parsedDateMilissec = Date.parse(viewValue); 16 | if (parsedDateMilissec > 0) { 17 | if (parsedDateMilissec >= minDate && parsedDateMilissec <= maxDate) { 18 | ctrl.$setValidity('date', true); 19 | return parsedDateMilissec; 20 | } 21 | } 22 | 23 | // in all other cases it is invalid, return undefined (no model update) 24 | ctrl.$setValidity('date', false); 25 | return undefined; 26 | }); 27 | 28 | ctrl.$formatters.unshift(function (modelValue) { 29 | return new Date(modelValue); 30 | }); 31 | } 32 | }; 33 | }]); -------------------------------------------------------------------------------- /ch.bfh.swos.bookapp.cqrs/src/main/java/ch/bfh/swos/bookapp/cqrs/author/application/command/AddAuthorCommand.java: -------------------------------------------------------------------------------- 1 | package ch.bfh.swos.bookapp.cqrs.author.application.command; 2 | 3 | import org.axonframework.commandhandling.annotation.TargetAggregateIdentifier; 4 | import org.axonframework.domain.IdentifierFactory; 5 | 6 | /** 7 | * Created with IntelliJ IDEA. 8 | * User: rovi 9 | * Date: 13.08.13 10 | * Time: 09:46 11 | * To change this template use File | Settings | File Templates. 12 | */ 13 | public class AddAuthorCommand { 14 | 15 | @TargetAggregateIdentifier 16 | private final String authorId; 17 | private final String firstname; 18 | private final String lastname; 19 | 20 | public AddAuthorCommand(String firstname, String lastname) { 21 | this.authorId = IdentifierFactory.getInstance().generateIdentifier(); 22 | this.firstname = firstname; 23 | this.lastname = lastname; 24 | } 25 | 26 | public AddAuthorCommand(String authorId, String firstname, String lastname) { 27 | this.authorId = authorId; 28 | this.firstname = firstname; 29 | this.lastname = lastname; 30 | } 31 | 32 | public String getAuthorId() { 33 | return authorId; 34 | } 35 | 36 | public String getFirstname() { 37 | return firstname; 38 | } 39 | 40 | public String getLastname() { 41 | return lastname; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /ch.bfh.swos.bookapp.query/src/main/java/ch/bfh/swos/bookapp/query/repository/impl/JdbcAuthorRepository.java: -------------------------------------------------------------------------------- 1 | package ch.bfh.swos.bookapp.query.repository.impl; 2 | 3 | import ch.bfh.swos.bookapp.query.dto.AuthorDTO; 4 | import ch.bfh.swos.bookapp.query.repository.AuthorRepository; 5 | import org.springframework.jdbc.core.BeanPropertyRowMapper; 6 | import org.springframework.jdbc.core.support.JdbcDaoSupport; 7 | import org.springframework.transaction.annotation.Transactional; 8 | 9 | import javax.inject.Inject; 10 | import javax.inject.Named; 11 | import javax.sql.DataSource; 12 | import java.util.Collection; 13 | 14 | /** 15 | * Created with IntelliJ IDEA. 16 | * User: rovi 17 | * Date: 12.10.13 18 | * Time: 11:41 19 | * To change this template use File | Settings | File Templates. 20 | */ 21 | @Named 22 | public class JdbcAuthorRepository extends JdbcDaoSupport implements AuthorRepository { 23 | 24 | @Inject 25 | public JdbcAuthorRepository(DataSource dataSource){ 26 | super(); 27 | setDataSource(dataSource); 28 | } 29 | 30 | @Override 31 | public Collection list() { 32 | return getJdbcTemplate().query("SELECT * FROM AUTHOR", 33 | new BeanPropertyRowMapper(AuthorDTO.class)); 34 | } 35 | 36 | @Override 37 | public void deleteAll() { 38 | getJdbcTemplate().update("DELETE FROM AUTHOR"); 39 | System.out.println("Deleted AUTHOR table"); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /ch.bfh.swos.bookapp.query/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | 8 | ch.bfh.swos.bookapp.parent 9 | ch.bfh.swos.bookapp 10 | 0.0.1-SNAPSHOT 11 | 12 | 13 | ch.bfh.swos.bookapp.query 14 | Bookapp Query 15 | 16 | 17 | 18 | org.springframework 19 | spring-jdbc 20 | 3.2.0.RELEASE 21 | 22 | 23 | org.springframework 24 | spring-context 25 | 3.2.0.RELEASE 26 | 27 | 28 | com.h2database 29 | h2 30 | 1.3.171 31 | compile 32 | 33 | 34 | javax.inject 35 | javax.inject 36 | 1 37 | compile 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /ch.bfh.swos.bookapp.cqrs/src/main/java/ch/bfh/swos/bookapp/cqrs/book/application/command/AddBookCommand.java: -------------------------------------------------------------------------------- 1 | package ch.bfh.swos.bookapp.cqrs.book.application.command; 2 | 3 | import org.axonframework.commandhandling.annotation.TargetAggregateIdentifier; 4 | import org.axonframework.domain.IdentifierFactory; 5 | 6 | import java.util.Date; 7 | 8 | /** 9 | * Created with IntelliJ IDEA. 10 | * User: rovi 11 | * Date: 13.08.13 12 | * Time: 09:46 13 | * To change this template use File | Settings | File Templates. 14 | */ 15 | public class AddBookCommand { 16 | 17 | @TargetAggregateIdentifier 18 | private final String bookId; 19 | private final String title; 20 | private final Date releaseDate; 21 | private final String authorId; 22 | 23 | public AddBookCommand(String title, Date releaseDate, String authorId) { 24 | this.bookId = IdentifierFactory.getInstance().generateIdentifier(); 25 | this.title = title; 26 | this.releaseDate = releaseDate; 27 | this.authorId = authorId; 28 | } 29 | 30 | public AddBookCommand(String bookId, String title, Date releaseDate, String authorId) { 31 | this.bookId = bookId; 32 | this.title = title; 33 | this.releaseDate = releaseDate; 34 | this.authorId = authorId; 35 | } 36 | 37 | public String getBookId() { 38 | return bookId; 39 | } 40 | 41 | public String getTitle() { 42 | return title; 43 | } 44 | 45 | public Date getReleaseDate() { 46 | return releaseDate; 47 | } 48 | 49 | public String getAuthorId() { 50 | return authorId; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /ch.bfh.swos.bookapp.jpa/src/main/java/ch/bfh/swos/bookapp/jpa/model/Book.java: -------------------------------------------------------------------------------- 1 | package ch.bfh.swos.bookapp.jpa.model; 2 | 3 | import javax.persistence.*; 4 | import java.io.Serializable; 5 | import java.util.Date; 6 | 7 | import static javax.persistence.GenerationType.IDENTITY; 8 | import static javax.persistence.TemporalType.DATE; 9 | 10 | /** 11 | * Entity implementation class for Entity: Book 12 | * 13 | */ 14 | @Entity 15 | public class Book implements Serializable { 16 | 17 | @Id 18 | @GeneratedValue(strategy = IDENTITY) 19 | private Long id; 20 | 21 | private String bookId; 22 | private String title; 23 | @Temporal(DATE) 24 | private Date releaseDate; 25 | private static final long serialVersionUID = 1L; 26 | 27 | @ManyToOne 28 | private Author author; 29 | 30 | public Book() { 31 | super(); 32 | } 33 | 34 | public Long getId() { 35 | return this.id; 36 | } 37 | 38 | public void setId(Long id) { 39 | this.id = id; 40 | } 41 | 42 | public String getBookId() { 43 | return bookId; 44 | } 45 | 46 | public void setBookId(String bookId) { 47 | this.bookId = bookId; 48 | } 49 | 50 | public String getTitle() { 51 | return this.title; 52 | } 53 | 54 | public void setTitle(String title) { 55 | this.title = title; 56 | } 57 | 58 | public Date getReleaseDate() { 59 | return this.releaseDate; 60 | } 61 | 62 | public void setReleaseDate(Date releaseDate) { 63 | this.releaseDate = releaseDate; 64 | } 65 | 66 | public Author getAuthor() { 67 | return author; 68 | } 69 | 70 | public void setAuthor(Author author) { 71 | this.author = author; 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /ch.bfh.swos.bookapp.jpa/src/main/resources/jpaContext.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 11 | 12 | 13 | 15 | 16 | 17 | 19 | 20 | 21 | 22 | 23 | 24 | static 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /ch.bfh.swos.bookapp.vertx/src/main/webapp/resources/js/services.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var services = angular.module('services', ['ngResource']); 4 | 5 | services.factory('Book', ['$resource', function ($resource) { 6 | return $resource('rest/books/:bookId', {bookId:'@id'}, { 7 | 'update': {method: 'PUT'} 8 | }); 9 | }]); 10 | 11 | services.factory('Author', ['$resource', function ($resource) { 12 | return $resource('rest/authors/:authorId', {authorId:'@id'}, { 13 | 'update': {method: 'PUT'} 14 | }); 15 | }]); 16 | 17 | services.factory('EventBus', ['$rootScope', function ($rootScope) { 18 | var eventBus = new vertx.EventBus("http://localhost:7777/eventbus"); 19 | $rootScope.connectionState = "WAITING..."; 20 | $rootScope.connectionStateClass = "badge-warning"; 21 | eventBus.onopen = function() { 22 | eventBus.registerHandler('client', function(msg, replyTo) { 23 | $rootScope.$broadcast(msg.eventId, msg); 24 | }); 25 | $rootScope.$apply(function() { 26 | $rootScope.connectionState = "CONNECTED"; 27 | $rootScope.connectionStateClass = "badge-success"; 28 | }); 29 | 30 | } 31 | eventBus.onclose = function() { 32 | $rootScope.$apply(function() { 33 | $rootScope.connectionState = "NOT CONNECTED"; 34 | $rootScope.connectionStateClass = "badge-important"; 35 | }); 36 | } 37 | return { 38 | emit: function(commandId, payload) { 39 | eventBus.send(commandId, payload, function (reply) { 40 | $.bootstrapGrowl(reply, {type:'info', offset: {from: 'top', amount: 50},width: 'auto'}); 41 | }); 42 | } 43 | }; 44 | }]); -------------------------------------------------------------------------------- /ch.bfh.swos.bookapp.cqrs/src/main/java/ch/bfh/swos/bookapp/cqrs/author/application/handler/AuthorCommandHandler.java: -------------------------------------------------------------------------------- 1 | package ch.bfh.swos.bookapp.cqrs.author.application.handler; 2 | 3 | import ch.bfh.swos.bookapp.cqrs.author.application.command.AddAuthorCommand; 4 | import ch.bfh.swos.bookapp.cqrs.author.application.command.RemoveAuthorCommand; 5 | import ch.bfh.swos.bookapp.cqrs.author.domain.Author; 6 | import org.axonframework.commandhandling.annotation.CommandHandler; 7 | import org.axonframework.repository.Repository; 8 | import org.springframework.stereotype.Component; 9 | 10 | import javax.inject.Inject; 11 | import javax.inject.Named; 12 | 13 | /** 14 | * Created with IntelliJ IDEA. 15 | * User: rovi 16 | * Date: 13.08.13 17 | * Time: 13:29 18 | * To change this template use File | Settings | File Templates. 19 | */ 20 | @Component 21 | public class AuthorCommandHandler { 22 | 23 | @Inject 24 | @Named("eventAuthorRepository") 25 | private Repository repository; 26 | 27 | 28 | public AuthorCommandHandler() { 29 | } 30 | 31 | public AuthorCommandHandler(Repository repository) { 32 | this.repository = repository; 33 | } 34 | 35 | @CommandHandler 36 | public void on(AddAuthorCommand command) { 37 | System.out.println("Axon received AddAuthorCommand"); 38 | Author author = new Author(command.getAuthorId(), command.getFirstname(), command.getLastname()); 39 | repository.add(author); 40 | } 41 | 42 | @CommandHandler 43 | public void on(RemoveAuthorCommand command) { 44 | System.out.println("Axon received RemoveAuthorCommand"); 45 | Author author = repository.load(command.getAuthorId()); 46 | author.remove(); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /ch.bfh.swos.bookapp.jpa/src/main/java/ch/bfh/swos/bookapp/jpa/repository/impl/JPABookRepository.java: -------------------------------------------------------------------------------- 1 | package ch.bfh.swos.bookapp.jpa.repository.impl; 2 | 3 | import ch.bfh.swos.bookapp.jpa.model.Book; 4 | import ch.bfh.swos.bookapp.jpa.repository.BookRepository; 5 | import org.springframework.transaction.annotation.Transactional; 6 | 7 | import javax.inject.Named; 8 | import javax.persistence.EntityManager; 9 | import javax.persistence.PersistenceContext; 10 | import javax.persistence.Query; 11 | import java.util.Collection; 12 | 13 | @Named 14 | public class JPABookRepository implements BookRepository { 15 | 16 | @PersistenceContext 17 | protected EntityManager em; 18 | 19 | @Override 20 | @Transactional 21 | public Book create(Book book) { 22 | em.persist(book); 23 | return book; 24 | } 25 | 26 | @Override 27 | public Book read(Long id) { 28 | return em.find(Book.class, id); 29 | } 30 | 31 | @Override 32 | @SuppressWarnings("unchecked") 33 | public Collection list() { 34 | return em.createQuery("select b from Book b").getResultList(); 35 | } 36 | 37 | @Override 38 | @Transactional 39 | public Book update(Book book) { 40 | return em.merge(book); 41 | } 42 | 43 | @Override 44 | @Transactional 45 | public void delete(Book book) { 46 | em.remove(em.merge(book)); 47 | } 48 | 49 | @Override 50 | @Transactional 51 | public void deleteAll() { 52 | em.createQuery("delete from Book").executeUpdate(); 53 | } 54 | 55 | @Override 56 | public Book readByBookId(String bookId) { 57 | Query query = em.createQuery("select book from Book book where book.bookId = :bookId"); 58 | query.setParameter("bookId", bookId); 59 | return (Book)query.getSingleResult(); 60 | } 61 | 62 | } 63 | -------------------------------------------------------------------------------- /ch.bfh.swos.bookapp.vertx/src/main/java/ch/bfh/swos/bookapp/vertx/VertXBean.java: -------------------------------------------------------------------------------- 1 | package ch.bfh.swos.bookapp.vertx; 2 | 3 | import org.springframework.stereotype.Service; 4 | import org.vertx.java.core.Vertx; 5 | import org.vertx.java.core.VertxFactory; 6 | import org.vertx.java.core.eventbus.EventBus; 7 | import org.vertx.java.core.http.HttpServer; 8 | import org.vertx.java.core.http.RouteMatcher; 9 | import org.vertx.java.core.json.JsonArray; 10 | import org.vertx.java.core.json.JsonObject; 11 | import org.vertx.java.core.sockjs.SockJSServer; 12 | 13 | /** 14 | * Created with IntelliJ IDEA. 15 | * User: rovi 16 | * Date: 21.08.13 17 | * Time: 14:23 18 | * To change this template use File | Settings | File Templates. 19 | */ 20 | @Service 21 | public class VertXBean { 22 | 23 | private final EventBus eventBus; 24 | 25 | private final Vertx vertx; 26 | 27 | public VertXBean() { 28 | super(); 29 | vertx = VertxFactory.newVertx(); 30 | start(); 31 | eventBus = vertx.eventBus(); 32 | } 33 | 34 | public void start() { 35 | 36 | RouteMatcher routeMatcher = new RouteMatcher(); 37 | 38 | // HTTP server 39 | HttpServer httpServer = vertx.createHttpServer(); 40 | httpServer.requestHandler(routeMatcher); 41 | 42 | // SockJS server 43 | JsonArray permitted = new JsonArray(); 44 | permitted.add(new JsonObject()); // Let everything through 45 | SockJSServer sockJSServer = vertx.createSockJSServer(httpServer); 46 | sockJSServer.bridge(new JsonObject().putString("prefix", "/eventbus"), permitted, permitted); 47 | 48 | httpServer.listen(7777); 49 | 50 | System.out.println("Vert.X Core UP"); 51 | } 52 | 53 | public EventBus getEventBus() { 54 | return eventBus; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /ch.bfh.swos.bookapp.vertx/src/main/java/ch/bfh/swos/bookapp/vertx/controller/ReplayController.java: -------------------------------------------------------------------------------- 1 | package ch.bfh.swos.bookapp.vertx.controller; 2 | 3 | import ch.bfh.swos.bookapp.cqrs.common.EventStorePlayer; 4 | import ch.bfh.swos.bookapp.query.repository.AuthorRepository; 5 | import ch.bfh.swos.bookapp.query.repository.BookRepository; 6 | import org.springframework.stereotype.Controller; 7 | import org.springframework.web.bind.annotation.RequestMapping; 8 | import org.springframework.web.bind.annotation.RequestMethod; 9 | import org.springframework.web.bind.annotation.ResponseBody; 10 | 11 | import javax.inject.Inject; 12 | 13 | /** 14 | * Created with IntelliJ IDEA. 15 | * User: rovi 16 | * Date: 20.09.13 17 | * Time: 09:37 18 | * To change this template use File | Settings | File Templates. 19 | */ 20 | @Controller 21 | @RequestMapping("/jpaviewcache") 22 | public class ReplayController { 23 | 24 | @Inject 25 | private EventStorePlayer eventStorePlayer; 26 | 27 | @Inject 28 | private BookRepository bookRepository; 29 | 30 | @Inject 31 | private AuthorRepository authorRepository; 32 | 33 | /** 34 | * Clean the Tables for the JPA ViewCache 35 | */ 36 | @RequestMapping(value = "clean", method = RequestMethod.GET) 37 | @ResponseBody 38 | public void cleanViewJPACache() { 39 | System.out.println("Cleaning the JPA View Cache"); 40 | bookRepository.deleteAll(); 41 | authorRepository.deleteAll(); 42 | } 43 | 44 | /** 45 | * Rebuild the Tables for the JPA ViewCache 46 | */ 47 | @RequestMapping(value = "rebuild", method = RequestMethod.GET) 48 | @ResponseBody 49 | public void rebuildFromEventStore() { 50 | System.out.println("Rebuilding the JPA View Cache"); 51 | eventStorePlayer.replay(); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /ch.bfh.swos.bookapp.cqrs/src/main/java/ch/bfh/swos/bookapp/cqrs/author/query/AuthorJPAEventListener.java: -------------------------------------------------------------------------------- 1 | package ch.bfh.swos.bookapp.cqrs.author.query; 2 | 3 | import ch.bfh.swos.bookapp.cqrs.author.domain.event.AuthorAddedEvent; 4 | import ch.bfh.swos.bookapp.cqrs.author.domain.event.AuthorRemovedEvent; 5 | import ch.bfh.swos.bookapp.jpa.model.Author; 6 | import ch.bfh.swos.bookapp.jpa.repository.AuthorRepository; 7 | import org.axonframework.eventhandling.annotation.EventHandler; 8 | import org.springframework.stereotype.Component; 9 | 10 | import javax.inject.Inject; 11 | 12 | /** 13 | * Created with IntelliJ IDEA. 14 | * User: rovi 15 | * Date: 12.08.13 16 | * Time: 14:18 17 | * To change this template use File | Settings | File Templates. 18 | */ 19 | @Component 20 | public class AuthorJPAEventListener { 21 | 22 | @Inject 23 | private AuthorRepository authorRepository; 24 | 25 | @EventHandler 26 | public void on(AuthorAddedEvent event) { 27 | System.out.println("JPA listener reveived author added event for: " + event.getFirstname() + " " + event.getLastname() + " (" + event.getAuthorId() + ")"); 28 | Author author = new Author(); 29 | author.setAuthorId(event.getAuthorId()); 30 | author.setFirstname(event.getFirstname()); 31 | author.setLastname(event.getLastname()); 32 | authorRepository.create(author); 33 | System.out.println("Author entity persisted"); 34 | } 35 | 36 | @EventHandler 37 | public void on(AuthorRemovedEvent event) { 38 | System.out.println("JPA listener reveived author removed event for: " + " (" + event.getAuthorId() + ")"); 39 | Author author = authorRepository.readByAuthorId(event.getAuthorId()); 40 | authorRepository.delete(author); 41 | System.out.println("Author entity deleted"); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /ch.bfh.swos.bookapp.jpa/src/main/java/ch/bfh/swos/bookapp/jpa/repository/impl/JPAAuthorRepository.java: -------------------------------------------------------------------------------- 1 | package ch.bfh.swos.bookapp.jpa.repository.impl; 2 | 3 | import ch.bfh.swos.bookapp.jpa.model.Author; 4 | import ch.bfh.swos.bookapp.jpa.repository.AuthorRepository; 5 | import org.springframework.transaction.annotation.Transactional; 6 | 7 | import javax.inject.Named; 8 | import javax.persistence.EntityManager; 9 | import javax.persistence.PersistenceContext; 10 | import javax.persistence.Query; 11 | import java.util.Collection; 12 | 13 | @Named 14 | public class JPAAuthorRepository implements AuthorRepository { 15 | 16 | @PersistenceContext 17 | protected EntityManager em; 18 | 19 | @Override 20 | @Transactional 21 | public Author create(Author author) { 22 | em.persist(author); 23 | return author; 24 | } 25 | 26 | @Override 27 | public Author read(long id) { 28 | return em.find(Author.class, id); 29 | } 30 | 31 | @Override 32 | @SuppressWarnings("unchecked") 33 | public Collection list() { 34 | return em.createQuery("select a from Author a").getResultList(); 35 | } 36 | 37 | @Override 38 | @Transactional 39 | public Author update(Author author) { 40 | return em.merge(author); 41 | } 42 | 43 | @Override 44 | @Transactional 45 | public void delete(Author author) { 46 | em.remove(em.merge(author)); 47 | } 48 | 49 | @Override 50 | @Transactional 51 | public void deleteAll() { 52 | em.createQuery("delete from Author").executeUpdate(); 53 | } 54 | 55 | @Override 56 | @Transactional 57 | public Author readByAuthorId(String authorId) { 58 | Query query = em.createQuery("select a from Author a where a.authorId = :authorId"); 59 | query.setParameter("authorId", authorId); 60 | return (Author)query.getSingleResult(); 61 | } 62 | 63 | } 64 | -------------------------------------------------------------------------------- /ch.bfh.swos.bookapp.vertx/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | 8 | ch.bfh.swos.bookapp.parent 9 | ch.bfh.swos.bookapp 10 | 0.0.1-SNAPSHOT 11 | 12 | 13 | ch.bfh.swos.bookapp.vertx 14 | Bookapp vert.x 15 | 16 | war 17 | 18 | 19 | 20 | ch.bfh.swos.bookapp 21 | ch.bfh.swos.bookapp.cqrs 22 | 0.0.1-SNAPSHOT 23 | compile 24 | 25 | 26 | ch.bfh.swos.bookapp 27 | ch.bfh.swos.bookapp.query 28 | 0.0.1-SNAPSHOT 29 | 30 | 31 | 32 | org.springframework 33 | spring-webmvc 34 | 3.2.0.RELEASE 35 | 36 | 37 | 38 | io.vertx 39 | vertx-core 40 | 2.0.1-final 41 | 42 | 43 | 44 | org.axonframework 45 | axon-core 46 | 2.0.3 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /ch.bfh.swos.bookapp.jpa/src/main/java/ch/bfh/swos/bookapp/jpa/model/Author.java: -------------------------------------------------------------------------------- 1 | package ch.bfh.swos.bookapp.jpa.model; 2 | 3 | import javax.persistence.Entity; 4 | import javax.persistence.GeneratedValue; 5 | import javax.persistence.Id; 6 | import javax.persistence.OneToMany; 7 | import java.io.Serializable; 8 | import java.util.Set; 9 | 10 | import static javax.persistence.CascadeType.PERSIST; 11 | import static javax.persistence.CascadeType.REMOVE; 12 | import static javax.persistence.GenerationType.IDENTITY; 13 | 14 | /** 15 | * Entity implementation class for Entity: Author 16 | * 17 | */ 18 | @Entity 19 | public class Author implements Serializable { 20 | 21 | @Id 22 | @GeneratedValue(strategy = IDENTITY) 23 | private long id; 24 | 25 | private String authorId; 26 | private String firstname; 27 | private String lastname; 28 | private static final long serialVersionUID = 1L; 29 | 30 | @OneToMany(mappedBy = "author", cascade = { PERSIST, REMOVE }) 31 | private Set books; 32 | 33 | public Author() { 34 | super(); 35 | } 36 | 37 | public long getId() { 38 | return this.id; 39 | } 40 | 41 | public void setId(long id) { 42 | this.id = id; 43 | } 44 | 45 | public String getAuthorId() { 46 | return authorId; 47 | } 48 | 49 | public void setAuthorId(String authorId) { 50 | this.authorId = authorId; 51 | } 52 | 53 | public String getFirstname() { 54 | return this.firstname; 55 | } 56 | 57 | public void setFirstname(String firstname) { 58 | this.firstname = firstname; 59 | } 60 | 61 | public String getLastname() { 62 | return this.lastname; 63 | } 64 | 65 | public void setLastname(String lastname) { 66 | this.lastname = lastname; 67 | } 68 | 69 | public Set getBooks() { 70 | return books; 71 | } 72 | 73 | public void setBooks(Set books) { 74 | this.books = books; 75 | } 76 | 77 | } 78 | -------------------------------------------------------------------------------- /ch.bfh.swos.bookapp.cqrs/src/main/java/ch/bfh/swos/bookapp/cqrs/author/domain/Author.java: -------------------------------------------------------------------------------- 1 | package ch.bfh.swos.bookapp.cqrs.author.domain; 2 | 3 | import ch.bfh.swos.bookapp.cqrs.author.domain.event.AuthorAddedEvent; 4 | import ch.bfh.swos.bookapp.cqrs.author.domain.event.AuthorRemovedEvent; 5 | import org.axonframework.eventhandling.annotation.EventHandler; 6 | import org.axonframework.eventsourcing.annotation.AbstractAnnotatedAggregateRoot; 7 | import org.axonframework.eventsourcing.annotation.AggregateIdentifier; 8 | 9 | /** 10 | * Created with IntelliJ IDEA. 11 | * User: rovi 12 | * Date: 13.08.13 13 | * Time: 10:39 14 | * To change this template use File | Settings | File Templates. 15 | */ 16 | public class Author extends AbstractAnnotatedAggregateRoot { 17 | 18 | @AggregateIdentifier 19 | private String authorId; 20 | 21 | private String firstname; 22 | private String lastname; 23 | 24 | public Author() { 25 | } 26 | 27 | public Author(String authorId, String firstname, String lastname) { 28 | // try { 29 | // Thread.sleep(10000); 30 | // } catch (InterruptedException e) { 31 | // e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates. 32 | // } 33 | apply(new AuthorAddedEvent(authorId, firstname, lastname)); 34 | } 35 | 36 | public void remove() { 37 | apply(new AuthorRemovedEvent(authorId)); 38 | } 39 | 40 | @EventHandler 41 | public void on(AuthorAddedEvent event) { 42 | System.out.println("AuthorAddedEvent called on Author"); 43 | this.authorId = event.getAuthorId(); 44 | this.firstname = event.getFirstname(); 45 | this.lastname = event.getLastname(); 46 | } 47 | 48 | @EventHandler 49 | public void on(AuthorRemovedEvent event) { 50 | System.out.println("AuthorRemovedEvent called on Author"); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /ch.bfh.swos.bookapp.cqrs/src/test/java/ch/bfh/swos/bookapp/cqrs/author/AuthorEventTest.java: -------------------------------------------------------------------------------- 1 | package ch.bfh.swos.bookapp.cqrs.author; 2 | 3 | import ch.bfh.swos.bookapp.cqrs.author.application.command.AddAuthorCommand; 4 | import ch.bfh.swos.bookapp.cqrs.author.application.command.RemoveAuthorCommand; 5 | import ch.bfh.swos.bookapp.cqrs.author.application.handler.AuthorCommandHandler; 6 | import ch.bfh.swos.bookapp.cqrs.author.domain.Author; 7 | import ch.bfh.swos.bookapp.cqrs.author.domain.event.AuthorAddedEvent; 8 | import ch.bfh.swos.bookapp.cqrs.author.domain.event.AuthorRemovedEvent; 9 | import org.axonframework.domain.IdentifierFactory; 10 | import org.axonframework.test.FixtureConfiguration; 11 | import org.axonframework.test.Fixtures; 12 | import org.junit.Before; 13 | import org.junit.Test; 14 | 15 | /** 16 | * Created with IntelliJ IDEA. 17 | * User: rovi 18 | * Date: 13.08.13 19 | * Time: 10:53 20 | * To change this template use File | Settings | File Templates. 21 | */ 22 | public class AuthorEventTest { 23 | 24 | private FixtureConfiguration fixture; 25 | private String authorId; 26 | 27 | @Before 28 | public void setUp() throws Exception { 29 | fixture = Fixtures.newGivenWhenThenFixture(Author.class); 30 | AuthorCommandHandler handler = new AuthorCommandHandler(fixture.getRepository()); 31 | fixture.registerAnnotatedCommandHandler(handler); 32 | authorId = IdentifierFactory.getInstance().generateIdentifier(); 33 | } 34 | 35 | @Test 36 | public void testAddAuthor() throws Exception { 37 | 38 | fixture.given() 39 | .when(new AddAuthorCommand(authorId, "H.R.R", "Tolkien")) 40 | .expectEvents(new AuthorAddedEvent(authorId, "H.R.R", "Tolkien")); 41 | } 42 | 43 | @Test 44 | public void testRemoveAuthor() throws Exception { 45 | fixture.given(new AuthorAddedEvent(authorId, "H.R.R", "Tolkien")) 46 | .when(new RemoveAuthorCommand(authorId)) 47 | .expectEvents(new AuthorRemovedEvent(authorId)); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /ch.bfh.swos.bookapp.cqrs/src/main/java/ch/bfh/swos/bookapp/cqrs/book/application/handler/BookCommandHandler.java: -------------------------------------------------------------------------------- 1 | package ch.bfh.swos.bookapp.cqrs.book.application.handler; 2 | 3 | import ch.bfh.swos.bookapp.cqrs.book.application.command.AddBookCommand; 4 | import ch.bfh.swos.bookapp.cqrs.book.application.command.ChangeBookTitleCommand; 5 | import ch.bfh.swos.bookapp.cqrs.book.application.command.RemoveBookCommand; 6 | import ch.bfh.swos.bookapp.cqrs.book.domain.Book; 7 | import org.axonframework.commandhandling.annotation.CommandHandler; 8 | import org.axonframework.repository.Repository; 9 | import org.springframework.stereotype.Component; 10 | 11 | import javax.inject.Inject; 12 | import javax.inject.Named; 13 | 14 | /** 15 | * Created with IntelliJ IDEA. 16 | * User: rovi 17 | * Date: 13.08.13 18 | * Time: 13:29 19 | * To change this template use File | Settings | File Templates. 20 | */ 21 | @Component 22 | public class BookCommandHandler { 23 | 24 | @Inject 25 | @Named("eventBookRepository") 26 | private Repository repository; 27 | 28 | public BookCommandHandler() { 29 | } 30 | 31 | public BookCommandHandler(Repository repository) { 32 | this.repository = repository; 33 | } 34 | 35 | @CommandHandler 36 | public void on(AddBookCommand command) { 37 | System.out.println("Axon received AddBookCommand"); 38 | Book book = new Book(command.getBookId(), command.getTitle(), command.getReleaseDate(), command.getAuthorId()); 39 | repository.add(book); 40 | } 41 | 42 | @CommandHandler 43 | public void on(ChangeBookTitleCommand command) { 44 | System.out.println("Axon received ChangeBookTitleCommand"); 45 | Book book = repository.load(command.getBookId()); 46 | book.changeTitle(command.getNewTitle()); 47 | } 48 | 49 | @CommandHandler 50 | public void on(RemoveBookCommand command) { 51 | System.out.println("Axon received RemoveBookCommand"); 52 | Book book = repository.load(command.getBookId()); 53 | book.remove(); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /ch.bfh.swos.bookapp.vertx/src/main/webapp/pages/authors.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 | New Author 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 |
13 |
14 |
15 | 16 |
17 |
18 | Authors 19 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 |
FirstnameLastnameIdActions
{{author.firstname}}{{author.lastname}}{{author.authorId}}
41 |
42 |
43 |
-------------------------------------------------------------------------------- /ch.bfh.swos.bookapp.vertx/src/main/java/ch/bfh/swos/bookapp/vertx/listener/AuthorEventListener.java: -------------------------------------------------------------------------------- 1 | package ch.bfh.swos.bookapp.vertx.listener; 2 | 3 | import ch.bfh.swos.bookapp.cqrs.author.domain.event.AuthorAddedEvent; 4 | import ch.bfh.swos.bookapp.cqrs.author.domain.event.AuthorRemovedEvent; 5 | import ch.bfh.swos.bookapp.vertx.VertXBean; 6 | import org.axonframework.eventhandling.annotation.EventHandler; 7 | import org.springframework.stereotype.Component; 8 | import org.vertx.java.core.json.JsonObject; 9 | 10 | import javax.inject.Inject; 11 | 12 | /** 13 | * Created with IntelliJ IDEA. 14 | * User: rovi 15 | * Date: 12.08.13 16 | * Time: 14:18 17 | * To change this template use File | Settings | File Templates. 18 | */ 19 | @Component 20 | public class AuthorEventListener { 21 | 22 | public static final String EVENT_AUTHOR_ADDED = "event.author.added"; 23 | public static final String EVENT_AUTHOR_REMOVED = "event.author.removed"; 24 | 25 | public static final String CLIENT_ADDRESS = "client"; 26 | 27 | @Inject 28 | private VertXBean vertxBean; 29 | 30 | @EventHandler 31 | public void on(AuthorAddedEvent event) { 32 | System.out.println("VertX listener reveived author added event for: " + event.getFirstname() + " " +event.getLastname() + " (" + event.getAuthorId() + ")"); 33 | JsonObject paylod = new JsonObject(); 34 | paylod.putString("eventId", EVENT_AUTHOR_ADDED); 35 | paylod.putString("authorId", event.getAuthorId()); 36 | paylod.putString("firstname", event.getFirstname()); 37 | paylod.putString("lastname", event.getLastname()); 38 | vertxBean.getEventBus().publish(CLIENT_ADDRESS, paylod); 39 | } 40 | 41 | @EventHandler 42 | public void on(AuthorRemovedEvent event) { 43 | System.out.println("VertX listener reveived author removed event for: " + event.getAuthorId()); 44 | JsonObject paylod = new JsonObject(); 45 | paylod.putString("eventId", EVENT_AUTHOR_REMOVED); 46 | paylod.putString("authorId", event.getAuthorId()); 47 | vertxBean.getEventBus().publish(CLIENT_ADDRESS,paylod); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /ch.bfh.swos.bookapp.vertx/src/main/webapp/resources/js/lib/vertxbus.min.js: -------------------------------------------------------------------------------- 1 | var vertx=vertx||{}; 2 | !function(j){"function"===typeof define&&define.amd?define("vertxbus",["sockjs"],j):j(SockJS)}(function(j){vertx.EventBus=function(p,q){function o(){e.send(JSON.stringify({type:"ping"}))}function l(b,a,c,f){g("address","string",a);g("replyHandler","function",f,!0);k();b={type:b,address:a,body:c};d.sessionID&&(b.sessionID=d.sessionID);f&&(a=r(),b.replyAddress=a,m[a]=f);f=JSON.stringify(b);e.send(f)}function k(){if(h!=vertx.EventBus.OPEN)throw Error("INVALID_STATE_ERR");}function g(b,a,c,f){if(!f&& 3 | !c)throw Error("Parameter "+b+" must be specified");if(c&&typeof c!=a)throw Error("Parameter "+b+" must be of type "+a);}function r(){return"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,function(b,a){return a=16*Math.random(),("y"==b?a&3|8:a|0).toString(16)})}var d=this,e=new j(p,void 0,q),i={},m={},h=vertx.EventBus.CONNECTING,n=null;d.onopen=null;d.onclose=null;d.login=function(b,a,c){l("send","vertx.basicauthmanager.login",{username:b,password:a},function(a){"ok"===a.status&&(d.sessionID= 4 | a.sessionID);c&&(delete a.sessionID,c(a))})};d.send=function(b,a,c){l("send",b,a,c)};d.publish=function(b,a,c){l("publish",b,a,c)};d.registerHandler=function(b,a){g("address","string",b);g("handler","function",a);k();var c=i[b];c?c[c.length]=a:(c=[a],i[b]=c,e.send(JSON.stringify({type:"register",address:b})))};d.unregisterHandler=function(b,a){g("address","string",b);g("handler","function",a);k();var c=i[b];if(c){var d=c.indexOf(a);-1!=d&&c.splice(d,1);0==c.length&&(e.send(JSON.stringify({type:"unregister", 5 | address:b})),delete i[b])}};d.close=function(){k();n&&clearInterval(n);h=vertx.EventBus.CLOSING;e.close()};d.readyState=function(){return h};e.onopen=function(){o();n=setInterval(o,5E3);h=vertx.EventBus.OPEN;if(d.onopen)d.onopen()};e.onclose=function(){h=vertx.EventBus.CLOSED;if(d.onclose)d.onclose()};e.onmessage=function(b){var a=JSON.parse(b.data),b=a.body,c=a.replyAddress,a=a.address,f;c&&(f=function(a,b){d.send(c,a,b)});var e=i[a];if(e){a=e.slice(0);for(e=0;e list() { 35 | return getJdbcTemplate().query("SELECT * FROM BOOK book, AUTHOR author WHERE book.AUTHOR_ID = author.ID", 36 | new BookRowMapper()); 37 | } 38 | 39 | @Override 40 | public void deleteAll() { 41 | getJdbcTemplate().update("DELETE FROM BOOK"); 42 | System.out.println("Deleted BOOK table"); 43 | } 44 | 45 | private class BookRowMapper implements RowMapper { 46 | 47 | @Override 48 | public BookDTO mapRow(ResultSet rs, int rowNum) throws SQLException { 49 | BookDTO book = new BookDTO(); 50 | book.setBookId(rs.getString("BOOKID")); 51 | book.setTitle(rs.getString("TITLE")); 52 | book.setReleaseDate(rs.getDate("RELEASEDATE")); 53 | 54 | AuthorDTO author = new AuthorDTO(); 55 | author.setAuthorId(rs.getString("AUTHORID")); 56 | author.setFirstname(rs.getString("FIRSTNAME")); 57 | author.setLastname(rs.getString("LASTNAME")); 58 | 59 | book.setAuthor(author); 60 | return book; 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /ch.bfh.swos.bookapp.jpa/pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4.0.0 4 | 5 | 6 | ch.bfh.swos.bookapp 7 | ch.bfh.swos.bookapp.parent 8 | 0.0.1-SNAPSHOT 9 | 10 | 11 | ch.bfh.swos.bookapp.jpa 12 | Bookapp JPA 13 | 14 | 15 | 16 | org.eclipse.persistence 17 | eclipselink 18 | 2.2.0 19 | compile 20 | 21 | 22 | org.eclipse.persistence 23 | javax.persistence 24 | 2.0.0 25 | compile 26 | 27 | 28 | com.h2database 29 | h2 30 | 1.3.171 31 | compile 32 | 33 | 34 | org.springframework 35 | spring-orm 36 | 3.2.0.RELEASE 37 | compile 38 | 39 | 40 | javax.inject 41 | javax.inject 42 | 1 43 | compile 44 | 45 | 46 | junit 47 | junit 48 | 4.5 49 | jar 50 | test 51 | 52 | 53 | org.springframework 54 | spring-test 55 | 3.2.0.RELEASE 56 | test 57 | 58 | 59 | org.axonframework 60 | axon-core 61 | 2.0.3 62 | 63 | 64 | 65 | 66 | 67 | EclipseLink 68 | http://www.eclipse.org/downloads/download.php?r=1&nf=1&file=/rt/eclipselink/maven.repo 69 | 70 | 71 | 72 | -------------------------------------------------------------------------------- /ch.bfh.swos.bookapp.cqrs/src/main/java/ch/bfh/swos/bookapp/cqrs/book/domain/Book.java: -------------------------------------------------------------------------------- 1 | package ch.bfh.swos.bookapp.cqrs.book.domain; 2 | 3 | import ch.bfh.swos.bookapp.cqrs.book.domain.event.BookAddedEvent; 4 | import ch.bfh.swos.bookapp.cqrs.book.domain.event.BookRemovedEvent; 5 | import ch.bfh.swos.bookapp.cqrs.book.domain.event.BookTitleChangedEvent; 6 | import org.axonframework.eventhandling.annotation.EventHandler; 7 | import org.axonframework.eventsourcing.annotation.AbstractAnnotatedAggregateRoot; 8 | import org.axonframework.eventsourcing.annotation.AggregateIdentifier; 9 | 10 | import java.util.Date; 11 | 12 | /** 13 | * Created with IntelliJ IDEA. 14 | * User: rovi 15 | * Date: 13.08.13 16 | * Time: 10:39 17 | * To change this template use File | Settings | File Templates. 18 | */ 19 | public class Book extends AbstractAnnotatedAggregateRoot { 20 | 21 | @AggregateIdentifier 22 | private String bookId; 23 | 24 | private String title; 25 | private Date releaseDate; 26 | 27 | private String authorId; 28 | 29 | public Book() { 30 | } 31 | 32 | public Book(String bookId, String title, Date releaseDate, String authorId) { 33 | // try { 34 | // Thread.sleep(5000); 35 | // } catch (InterruptedException e) { 36 | // e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates. 37 | // } 38 | apply(new BookAddedEvent(bookId, title, releaseDate, authorId)); 39 | } 40 | 41 | public void changeTitle(String newTitle) { 42 | // validation 43 | if (!newTitle.isEmpty()) { 44 | apply(new BookTitleChangedEvent(bookId, newTitle)); 45 | } 46 | } 47 | 48 | public void remove() { 49 | apply(new BookRemovedEvent(bookId)); 50 | } 51 | 52 | @EventHandler 53 | public void on(BookAddedEvent event) { 54 | System.out.println("BookAddedEvent called on Book"); 55 | this.bookId = event.getBookId(); 56 | this.title = event.getTitle(); 57 | this.releaseDate = event.getReleaseDate(); 58 | this.authorId = event.getAuthorId(); 59 | } 60 | 61 | @EventHandler 62 | public void on(BookTitleChangedEvent event) { 63 | System.out.println("BookTitleChangedEvent called on Book"); 64 | this.bookId = event.getBookId(); 65 | this.title = event.getNewTitle(); 66 | } 67 | 68 | @EventHandler 69 | public void on(BookRemovedEvent event) { 70 | System.out.println("BookRemovedEvent called on Book"); 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /ch.bfh.swos.bookapp.cqrs/src/main/java/ch/bfh/swos/bookapp/cqrs/book/query/BookJPAEventListener.java: -------------------------------------------------------------------------------- 1 | package ch.bfh.swos.bookapp.cqrs.book.query; 2 | 3 | import ch.bfh.swos.bookapp.cqrs.book.domain.event.BookAddedEvent; 4 | import ch.bfh.swos.bookapp.cqrs.book.domain.event.BookRemovedEvent; 5 | import ch.bfh.swos.bookapp.cqrs.book.domain.event.BookTitleChangedEvent; 6 | import ch.bfh.swos.bookapp.jpa.model.Author; 7 | import ch.bfh.swos.bookapp.jpa.model.Book; 8 | import ch.bfh.swos.bookapp.jpa.repository.AuthorRepository; 9 | import ch.bfh.swos.bookapp.jpa.repository.BookRepository; 10 | import org.axonframework.eventhandling.annotation.EventHandler; 11 | import org.springframework.stereotype.Component; 12 | 13 | import javax.inject.Inject; 14 | 15 | /** 16 | * Created with IntelliJ IDEA. 17 | * User: rovi 18 | * Date: 12.08.13 19 | * Time: 14:18 20 | * To change this template use File | Settings | File Templates. 21 | */ 22 | @Component 23 | public class BookJPAEventListener { 24 | 25 | @Inject 26 | private BookRepository bookRepository; 27 | 28 | @Inject 29 | private AuthorRepository authorRepository; 30 | 31 | @EventHandler 32 | public void on(BookAddedEvent event) { 33 | System.out.println("JPA listener received book added event for: " + event.getTitle() + " (" + event.getBookId() + ")"); 34 | Book book = new Book(); 35 | book.setBookId(event.getBookId()); 36 | book.setTitle(event.getTitle()); 37 | book.setReleaseDate(event.getReleaseDate()); 38 | Author author = authorRepository.readByAuthorId(event.getAuthorId()); 39 | book.setAuthor(author); 40 | bookRepository.create(book); 41 | System.out.println("Book entity persisted"); 42 | } 43 | 44 | @EventHandler 45 | public void on(BookTitleChangedEvent event) { 46 | System.out.println("JPA listener received book title changed event for: " + event.getNewTitle() + " (" + event.getBookId() + ")"); 47 | Book book = bookRepository.readByBookId(event.getBookId()); 48 | book.setTitle(event.getNewTitle()); 49 | bookRepository.update(book); 50 | System.out.println("Book entity updated"); 51 | } 52 | 53 | @EventHandler 54 | public void on(BookRemovedEvent event) { 55 | System.out.println("JPA listener received book removed event for: " + " (" + event.getBookId() + ")"); 56 | Book book = bookRepository.readByBookId(event.getBookId()); 57 | bookRepository.delete(book); 58 | System.out.println("Book entity deleted"); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /ch.bfh.swos.bookapp.vertx/src/main/webapp/resources/js/lib/angular-resource.min.js: -------------------------------------------------------------------------------- 1 | /* 2 | AngularJS v1.0.7 3 | (c) 2010-2012 Google, Inc. http://angularjs.org 4 | License: MIT 5 | */ 6 | (function(C,d,w){'use strict';d.module("ngResource",["ng"]).factory("$resource",["$http","$parse",function(x,y){function s(b,e){return encodeURIComponent(b).replace(/%40/gi,"@").replace(/%3A/gi,":").replace(/%24/g,"$").replace(/%2C/gi,",").replace(/%20/g,e?"%20":"+")}function t(b,e){this.template=b+="#";this.defaults=e||{};var a=this.urlParams={};h(b.split(/\W/),function(f){f&&RegExp("(^|[^\\\\]):"+f+"\\W").test(b)&&(a[f]=!0)});this.template=b.replace(/\\:/g,":")}function u(b,e,a){function f(m,a){var b= 7 | {},a=o({},e,a);h(a,function(a,z){var c;a.charAt&&a.charAt(0)=="@"?(c=a.substr(1),c=y(c)(m)):c=a;b[z]=c});return b}function g(a){v(a||{},this)}var k=new t(b),a=o({},A,a);h(a,function(a,b){a.method=d.uppercase(a.method);var e=a.method=="POST"||a.method=="PUT"||a.method=="PATCH";g[b]=function(b,c,d,B){var j={},i,l=p,q=null;switch(arguments.length){case 4:q=B,l=d;case 3:case 2:if(r(c)){if(r(b)){l=b;q=c;break}l=c;q=d}else{j=b;i=c;l=d;break}case 1:r(b)?l=b:e?i=b:j=b;break;case 0:break;default:throw"Expected between 0-4 arguments [params, data, success, error], got "+ 8 | arguments.length+" arguments.";}var n=this instanceof g?this:a.isArray?[]:new g(i);x({method:a.method,url:k.url(o({},f(i,a.params||{}),j)),data:i}).then(function(b){var c=b.data;if(c)a.isArray?(n.length=0,h(c,function(a){n.push(new g(a))})):v(c,n);(l||p)(n,b.headers)},q);return n};g.prototype["$"+b]=function(a,d,h){var m=f(this),j=p,i;switch(arguments.length){case 3:m=a;j=d;i=h;break;case 2:case 1:r(a)?(j=a,i=d):(m=a,j=d||p);case 0:break;default:throw"Expected between 1-3 arguments [params, success, error], got "+ 9 | arguments.length+" arguments.";}g[b].call(this,m,e?this:w,j,i)}});g.bind=function(d){return u(b,o({},e,d),a)};return g}var A={get:{method:"GET"},save:{method:"POST"},query:{method:"GET",isArray:!0},remove:{method:"DELETE"},"delete":{method:"DELETE"}},p=d.noop,h=d.forEach,o=d.extend,v=d.copy,r=d.isFunction;t.prototype={url:function(b){var e=this,a=this.template,f,g,b=b||{};h(this.urlParams,function(h,c){f=b.hasOwnProperty(c)?b[c]:e.defaults[c];d.isDefined(f)&&f!==null?(g=s(f,!0).replace(/%26/gi,"&").replace(/%3D/gi, 10 | "=").replace(/%2B/gi,"+"),a=a.replace(RegExp(":"+c+"(\\W)","g"),g+"$1")):a=a.replace(RegExp("(/?):"+c+"(\\W)","g"),function(a,b,c){return c.charAt(0)=="/"?c:b+c})});var a=a.replace(/\/?#$/,""),k=[];h(b,function(a,b){e.urlParams[b]||k.push(s(b)+"="+s(a))});k.sort();a=a.replace(/\/*$/,"");return a+(k.length?"?"+k.join("&"):"")}};return u}])})(window,window.angular); 11 | -------------------------------------------------------------------------------- /ch.bfh.swos.bookapp.cqrs/pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4.0.0 4 | 5 | 6 | ch.bfh.swos.bookapp.parent 7 | ch.bfh.swos.bookapp 8 | 0.0.1-SNAPSHOT 9 | 10 | 11 | ch.bfh.swos.bookapp.cqrs 12 | Bookapp CQRS 13 | 14 | 15 | 16 | ch.bfh.swos.bookapp 17 | ch.bfh.swos.bookapp.jpa 18 | 0.0.1-SNAPSHOT 19 | compile 20 | 21 | 22 | 23 | org.axonframework 24 | axon-core 25 | 2.0.3 26 | 27 | 28 | 29 | org.springframework 30 | spring-context 31 | 3.2.0.RELEASE 32 | 33 | 34 | javax.inject 35 | javax.inject 36 | 1 37 | compile 38 | 39 | 40 | 41 | org.axonframework 42 | axon-mongo 43 | 2.0.3 44 | 45 | 46 | org.mongodb 47 | mongo-java-driver 48 | 2.11.2 49 | 50 | 51 | 52 | junit 53 | junit 54 | 4.5 55 | test 56 | 57 | 58 | org.springframework 59 | spring-test 60 | 3.2.0.RELEASE 61 | test 62 | 63 | 64 | org.axonframework 65 | axon-test 66 | 2.0.3 67 | test 68 | 69 | 70 | 71 | -------------------------------------------------------------------------------- /ch.bfh.swos.bookapp.cqrs/src/test/java/ch/bfh/swos/bookapp/cqrs/book/BookEventTest.java: -------------------------------------------------------------------------------- 1 | package ch.bfh.swos.bookapp.cqrs.book; 2 | 3 | import ch.bfh.swos.bookapp.cqrs.book.application.command.AddBookCommand; 4 | import ch.bfh.swos.bookapp.cqrs.book.application.command.ChangeBookTitleCommand; 5 | import ch.bfh.swos.bookapp.cqrs.book.application.command.RemoveBookCommand; 6 | import ch.bfh.swos.bookapp.cqrs.book.application.handler.BookCommandHandler; 7 | import ch.bfh.swos.bookapp.cqrs.book.domain.Book; 8 | import ch.bfh.swos.bookapp.cqrs.book.domain.event.BookAddedEvent; 9 | import ch.bfh.swos.bookapp.cqrs.book.domain.event.BookRemovedEvent; 10 | import ch.bfh.swos.bookapp.cqrs.book.domain.event.BookTitleChangedEvent; 11 | import org.axonframework.domain.IdentifierFactory; 12 | import org.axonframework.test.FixtureConfiguration; 13 | import org.axonframework.test.Fixtures; 14 | import org.junit.Before; 15 | import org.junit.Test; 16 | 17 | /** 18 | * Created with IntelliJ IDEA. 19 | * User: rovi 20 | * Date: 13.08.13 21 | * Time: 10:53 22 | * To change this template use File | Settings | File Templates. 23 | */ 24 | public class BookEventTest { 25 | 26 | private FixtureConfiguration fixture; 27 | private String bookId; 28 | 29 | @Before 30 | public void setUp() throws Exception { 31 | fixture = Fixtures.newGivenWhenThenFixture(Book.class); 32 | BookCommandHandler handler = new BookCommandHandler(fixture.getRepository()); 33 | fixture.registerAnnotatedCommandHandler(handler); 34 | bookId = IdentifierFactory.getInstance().generateIdentifier(); 35 | } 36 | 37 | @Test 38 | public void testAddBook() throws Exception { 39 | fixture.given() 40 | .when(new AddBookCommand(bookId, "Lord of the Rings - Return of the King", null, null)) 41 | .expectEvents(new BookAddedEvent(bookId, "Lord of the Rings - Return of the King", null, null)); 42 | } 43 | 44 | @Test 45 | public void testChangeBookTitle() throws Exception { 46 | fixture.given(new BookAddedEvent(bookId, "Lord of the Rings - Return of the King", null, null)) 47 | .when(new ChangeBookTitleCommand(bookId, "Lord of the Rings - Fellowship of the Ring")) 48 | .expectEvents(new BookTitleChangedEvent(bookId, "Lord of the Rings - Fellowship of the Ring")); 49 | } 50 | 51 | @Test 52 | public void testRemoveBook() throws Exception { 53 | fixture.given(new BookAddedEvent(bookId, "Lord of the Rings - Return of the King", null, null)) 54 | .when(new RemoveBookCommand(bookId)) 55 | .expectEvents(new BookRemovedEvent(bookId)); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /ch.bfh.swos.bookapp.vertx/src/main/java/ch/bfh/swos/bookapp/vertx/handler/AuthorCommandHandler.java: -------------------------------------------------------------------------------- 1 | package ch.bfh.swos.bookapp.vertx.handler; 2 | 3 | import ch.bfh.swos.bookapp.cqrs.author.application.command.AddAuthorCommand; 4 | import ch.bfh.swos.bookapp.cqrs.author.application.command.RemoveAuthorCommand; 5 | import ch.bfh.swos.bookapp.vertx.VertXBean; 6 | import org.axonframework.commandhandling.gateway.CommandGateway; 7 | import org.springframework.stereotype.Component; 8 | import org.vertx.java.core.Handler; 9 | import org.vertx.java.core.eventbus.Message; 10 | import org.vertx.java.core.json.JsonObject; 11 | 12 | import javax.annotation.PostConstruct; 13 | import javax.inject.Inject; 14 | import javax.inject.Named; 15 | 16 | /** 17 | * Created with IntelliJ IDEA. 18 | * User: rovi 19 | * Date: 26.08.13 20 | * Time: 14:16 21 | * To change this template use File | Settings | File Templates. 22 | */ 23 | @Component 24 | @Named("vertxAuthorCommandHandler") 25 | public class AuthorCommandHandler { 26 | 27 | public static final String COMMAND_ADD_AUTHOR = "command.add.author"; 28 | public static final String COMMAND_REMOVE_AUTHOR = "command.remove.author"; 29 | 30 | @Inject 31 | private VertXBean vertx; 32 | 33 | @Inject 34 | private CommandGateway commandGateway; 35 | 36 | @PostConstruct 37 | private void init() { 38 | vertx.getEventBus().registerHandler(COMMAND_ADD_AUTHOR, new Handler>() { 39 | @Override 40 | public void handle(Message payload) { 41 | System.out.println("VertX received author add command for: "+payload.body().getString("firstname")+ " " + payload.body().getString("lastname")); 42 | payload.reply("Received author add command for: "+payload.body().getString("firstname")+ " " + payload.body().getString("lastname")); 43 | commandGateway.send(new AddAuthorCommand(payload.body().getString("firstname"), payload.body().getString("lastname"))); 44 | } 45 | }); 46 | 47 | vertx.getEventBus().registerHandler(COMMAND_REMOVE_AUTHOR, new Handler>() { 48 | @Override 49 | public void handle(Message payload) { 50 | System.out.println("VertX received author remove command for: " + payload.body().getString("authorId")); 51 | payload.reply("Received author remove command for id: " + payload.body().getString("authorId")); 52 | commandGateway.send(new RemoveAuthorCommand(payload.body().getString("authorId"))); 53 | } 54 | }); 55 | System.out.println("VertX AuhtorCommandHandler initialized"); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /ch.bfh.swos.bookapp.vertx/src/main/java/ch/bfh/swos/bookapp/vertx/listener/BookEventListener.java: -------------------------------------------------------------------------------- 1 | package ch.bfh.swos.bookapp.vertx.listener; 2 | 3 | import ch.bfh.swos.bookapp.cqrs.book.domain.event.BookAddedEvent; 4 | import ch.bfh.swos.bookapp.cqrs.book.domain.event.BookRemovedEvent; 5 | import ch.bfh.swos.bookapp.cqrs.book.domain.event.BookTitleChangedEvent; 6 | import ch.bfh.swos.bookapp.vertx.VertXBean; 7 | import org.axonframework.eventhandling.annotation.EventHandler; 8 | import org.springframework.stereotype.Component; 9 | import org.vertx.java.core.json.JsonObject; 10 | 11 | import javax.inject.Inject; 12 | 13 | /** 14 | * Created with IntelliJ IDEA. 15 | * User: rovi 16 | * Date: 12.08.13 17 | * Time: 14:18 18 | * To change this template use File | Settings | File Templates. 19 | */ 20 | @Component 21 | public class BookEventListener { 22 | 23 | public static final String EVENT_BOOK_ADDED = "event.book.added"; 24 | public static final String EVENT_BOOK_REMOVED = "event.book.removed"; 25 | public static final String EVENT_BOOK_TITLE_CHANGED = "event.book.title.changed"; 26 | 27 | public static final String CLIENT_ADDRESS = "client"; 28 | 29 | @Inject 30 | private VertXBean vertxBean; 31 | 32 | @EventHandler 33 | public void on(BookAddedEvent event) { 34 | System.out.println("VertX listener reveived book added event for: " + event.getTitle() + " (" + event.getBookId() + ")"); 35 | JsonObject paylod = new JsonObject(); 36 | paylod.putString("eventId", EVENT_BOOK_ADDED); 37 | paylod.putString("bookId", event.getBookId()); 38 | paylod.putString("bookTitle", event.getTitle()); 39 | paylod.putNumber("releaseDate", event.getReleaseDate().getTime()); 40 | paylod.putString("authorId", event.getAuthorId()); 41 | vertxBean.getEventBus().publish(CLIENT_ADDRESS, paylod); 42 | } 43 | 44 | @EventHandler 45 | public void on(BookRemovedEvent event) { 46 | System.out.println("VertX listener reveived book removed event for: " + event.getBookId()); 47 | JsonObject paylod = new JsonObject(); 48 | paylod.putString("eventId", EVENT_BOOK_REMOVED); 49 | paylod.putString("bookId", event.getBookId()); 50 | vertxBean.getEventBus().publish(CLIENT_ADDRESS,paylod); 51 | } 52 | 53 | @EventHandler 54 | public void on(BookTitleChangedEvent event) { 55 | System.out.println("VertX listener reveived book title cahnged event for: " + event.getBookId()); 56 | JsonObject paylod = new JsonObject(); 57 | paylod.putString("eventId", EVENT_BOOK_TITLE_CHANGED); 58 | paylod.putString("bookId", event.getBookId()); 59 | paylod.putString("newBookTitle", event.getNewTitle()); 60 | vertxBean.getEventBus().publish(CLIENT_ADDRESS,paylod); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /ch.bfh.swos.bookapp.cqrs/src/main/resources/cqrsContext.xml: -------------------------------------------------------------------------------- 1 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 21 | 22 | 24 | 25 | 29 | 30 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /ch.bfh.swos.bookapp.vertx/src/main/webapp/index.html: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | BFH BookApp Vert.X 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 52 | 53 |
54 | 55 |
56 |
57 |
58 | 59 |
60 |
61 |

BookApp

62 |

This is the BFH BookApp example website.

63 |
64 |
65 |
66 | 67 |
68 |
69 | 70 | 71 | -------------------------------------------------------------------------------- /ch.bfh.swos.bookapp.vertx/src/main/java/ch/bfh/swos/bookapp/vertx/handler/BookCommandHandler.java: -------------------------------------------------------------------------------- 1 | package ch.bfh.swos.bookapp.vertx.handler; 2 | 3 | import ch.bfh.swos.bookapp.cqrs.book.application.command.AddBookCommand; 4 | import ch.bfh.swos.bookapp.cqrs.book.application.command.ChangeBookTitleCommand; 5 | import ch.bfh.swos.bookapp.cqrs.book.application.command.RemoveBookCommand; 6 | import ch.bfh.swos.bookapp.vertx.VertXBean; 7 | import org.axonframework.commandhandling.gateway.CommandGateway; 8 | import org.springframework.stereotype.Component; 9 | import org.vertx.java.core.Handler; 10 | import org.vertx.java.core.eventbus.Message; 11 | import org.vertx.java.core.json.JsonObject; 12 | 13 | import javax.annotation.PostConstruct; 14 | import javax.inject.Inject; 15 | import javax.inject.Named; 16 | import java.util.Date; 17 | 18 | /** 19 | * Created with IntelliJ IDEA. 20 | * User: rovi 21 | * Date: 26.08.13 22 | * Time: 14:16 23 | * To change this template use File | Settings | File Templates. 24 | */ 25 | @Component 26 | @Named("vertxBookCommandHandler") 27 | public class BookCommandHandler { 28 | 29 | public static final String COMMAND_ADD_BOOK = "command.add.book"; 30 | public static final String COMMAND_REMOVE_BOOK = "command.remove.book"; 31 | public static final String COMMAND_CHANGE_BOOK_TITLE = "command.change.book.title"; 32 | 33 | @Inject 34 | private VertXBean vertx; 35 | 36 | @Inject 37 | private CommandGateway commandGateway; 38 | 39 | public BookCommandHandler() { 40 | } 41 | 42 | @PostConstruct 43 | private void init() { 44 | vertx.getEventBus().registerHandler(COMMAND_ADD_BOOK, new Handler>() { 45 | @Override 46 | public void handle(Message payload) { 47 | System.out.println("VertX received book add command for: " + payload.body().getString("bookTitle")); 48 | payload.reply("Received book add command for: " + payload.body().getString("bookTitle")); 49 | commandGateway.send(new AddBookCommand(payload.body().getString("bookTitle"), new Date(payload.body().getLong("releaseDate")), payload.body().getString("authorId"))); 50 | } 51 | }); 52 | 53 | vertx.getEventBus().registerHandler(COMMAND_REMOVE_BOOK, new Handler>() { 54 | @Override 55 | public void handle(Message payload) { 56 | System.out.println("VertX received book remove command for: " + payload.body().getString("bookId")); 57 | payload.reply("Received book remove command for id: " + payload.body().getString("bookId")); 58 | commandGateway.send(new RemoveBookCommand(payload.body().getString("bookId"))); 59 | } 60 | }); 61 | 62 | vertx.getEventBus().registerHandler(COMMAND_CHANGE_BOOK_TITLE, new Handler>() { 63 | @Override 64 | public void handle(Message payload) { 65 | System.out.println("VertX received change book title command for: " + payload.body().getString("bookId")); 66 | payload.reply("Received change book title command for id: " + payload.body().getString("bookId")); 67 | commandGateway.send(new ChangeBookTitleCommand(payload.body().getString("bookId"), payload.body().getString("newBookTitle"))); 68 | } 69 | }); 70 | System.out.println("VertX BookCommandHandler initialized"); 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /ch.bfh.swos.bookapp.vertx/src/main/webapp/pages/books.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 | New Book 5 | 6 | 7 | 8 | 9 | 10 | 13 |
14 | 15 | 16 |
17 |
18 |
19 | 20 |
21 |
22 | Books 23 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 |
TitelRelease DateAuthorIdActions
{{book.title}}{{book.releaseDate | date:'dd.MM.yyyy'}}{{book.author.firstname }} {{book.author.lastname}}{{book.bookId}}
47 |
48 |
49 | 50 | 68 | 69 |
-------------------------------------------------------------------------------- /ch.bfh.swos.bookapp.cqrs/ch.bfh.swos.bookapp.cqrs.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | file://$MODULE_DIR$/src/main/resources/cqrsContext.xml 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | -------------------------------------------------------------------------------- /ch.bfh.swos.bookapp.vertx/src/main/webapp/resources/js/controllers.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var controllers = angular.module('controllers', ['services']); 4 | 5 | controllers.controller('BookController', ['$scope', 'Book', 'Author', 'EventBus', function($scope, Book, Author, EventBus) { 6 | $scope.currentBook = new Book(); 7 | $scope.currentBook.releaseDate = new Date().getTime(); 8 | $scope.books = Book.query(); 9 | $scope.authors = Author.query(); 10 | $scope.showId = false; 11 | 12 | // Book added event 13 | $scope.$on('event.book.added', function(event, msg) { 14 | $.bootstrapGrowl("Received book added event for: "+msg.bookTitle, {offset: {from: 'top', amount: 50},width: 'auto'}); 15 | var book = new Book(); 16 | book.bookId = msg.bookId; 17 | book.title = msg.bookTitle; 18 | book.releaseDate = msg.releaseDate; 19 | book.author = filterAuthorsById($scope.authors, msg.authorId); 20 | $scope.$apply( function() { 21 | $scope.books.push(book); 22 | }); 23 | }); 24 | 25 | // Book removed event 26 | $scope.$on('event.book.removed', function(event, msg) { 27 | $.bootstrapGrowl("Received book removed event for: "+msg.bookId, {offset: {from: 'top', amount: 50},width: 'auto'}); 28 | var i = $scope.books.length; 29 | while (i--){ 30 | if ($scope.books[i].bookId == msg.bookId){ 31 | $scope.$apply( function() { 32 | $scope.books.splice(i, 1); 33 | }); 34 | } 35 | } 36 | }); 37 | 38 | // Book title changed event 39 | $scope.$on('event.book.title.changed', function(event, msg) { 40 | $.bootstrapGrowl("Received book title changed event for: "+msg.bookId, {offset: {from: 'top', amount: 50},width: 'auto'}); 41 | var i = $scope.books.length; 42 | while (i--){ 43 | if ($scope.books[i].bookId == msg.bookId){ 44 | $scope.$apply( function() { 45 | $scope.books[i].title = msg.newBookTitle; 46 | }); 47 | } 48 | } 49 | }); 50 | 51 | $scope.cancel = function () { 52 | $scope.currentBook = new Book(); 53 | $scope.currentBook.releaseDate = new Date().getTime(); 54 | }; 55 | 56 | $scope.changeTitle = function(book) { 57 | $scope.$apply(new function() { 58 | $scope.selectedBookId = book.bookId; 59 | $scope.newTitle = book.title; 60 | }); 61 | $('#changeTitleModal').modal('show') 62 | } 63 | 64 | $scope.sendAddBookCommand = function () { 65 | EventBus.emit("command.add.book", {bookTitle: $scope.currentBook.title, releaseDate: $scope.currentBook.releaseDate, authorId: $scope.currentBook.author.authorId}); 66 | $scope.cancel(); 67 | }; 68 | 69 | $scope.sendChangeBookTitleCommand = function (bookId, newBookTitle) { 70 | EventBus.emit("command.change.book.title", {bookId: bookId, newBookTitle: newBookTitle}); 71 | }; 72 | 73 | $scope.sendRemoveBookCommand = function (bookId) { 74 | EventBus.emit("command.remove.book", {bookId: bookId}); 75 | }; 76 | }]); 77 | 78 | controllers.controller('AuthorController', ['$scope', 'Author', 'EventBus', '$rootScope', function($scope, Author, EventBus, $rootScope) { 79 | $scope.currentAuthor = new Author(); 80 | $scope.authors = Author.query(); 81 | $scope.showId = false; 82 | 83 | // Author added event 84 | $scope.$on('event.author.added', function(event, msg) { 85 | $.bootstrapGrowl("Received author added event for: "+msg.firstname+" "+msg.lastname, {offset: {from: 'top', amount: 50},width: 'auto'}); 86 | var author = new Author(); 87 | author.authorId = msg.authorId; 88 | author.firstname = msg.firstname; 89 | author.lastname = msg.lastname; 90 | $scope.$apply( function() { 91 | $scope.authors.push(author); 92 | }); 93 | }); 94 | 95 | // Author removed event 96 | $scope.$on('event.author.removed', function(event, msg) { 97 | $.bootstrapGrowl("Receifed author removed event for: "+msg.authorId, {offset: {from: 'top', amount: 50},width: 'auto'}); 98 | var i = $scope.authors.length; 99 | while (i--){ 100 | if ($scope.authors[i].authorId == msg.authorId){ 101 | $scope.$apply( function() { 102 | $scope.authors.splice(i, 1); 103 | }); 104 | } 105 | } 106 | }); 107 | 108 | $scope.cancel = function () { 109 | $scope.currentAuthor = new Author(); 110 | }; 111 | 112 | $scope.sendAddAuthorCommand = function () { 113 | EventBus.emit("command.add.author", {firstname: $scope.currentAuthor.firstname, lastname: $scope.currentAuthor.lastname}); 114 | $scope.cancel(); 115 | }; 116 | 117 | $scope.sendRemoveAuthorCommand = function (authorId) { 118 | EventBus.emit("command.remove.author", {authorId: authorId}); 119 | }; 120 | }]); 121 | 122 | controllers.controller('NavController', ['$scope', '$rootScope', '$route', '$http' , function($scope, $rootScope, $route, $http) { 123 | $rootScope.route = $route; 124 | 125 | $scope.clean = function() { 126 | $http({method: 'GET', url: '/rest/jpaviewcache/clean'}) 127 | } 128 | 129 | $scope.rebuild = function() { 130 | $http({method: 'GET', url: '/rest/jpaviewcache/rebuild'}) 131 | } 132 | }]); 133 | 134 | function filterAuthorsById(array, id) { 135 | return array.filter(function (author) { 136 | return author.authorId == id; 137 | })[0]; 138 | } -------------------------------------------------------------------------------- /ch.bfh.swos.bookapp.vertx/ch.bfh.swos.bookapp.vertx.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | file://$MODULE_DIR$/src/main/webapp/WEB-INF/rest-servlet.xml 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | -------------------------------------------------------------------------------- /ch.bfh.swos.bookapp.jpa/ch.bfh.swos.bookapp.jpa.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | file://$MODULE_DIR$/../ch.bfh.swos.bookapp.domain/src/main/resources/persistenceContext.xml 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, and 10 | distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by the copyright 13 | owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all other entities 16 | that control, are controlled by, or are under common control with that entity. 17 | For the purposes of this definition, "control" means (i) the power, direct or 18 | indirect, to cause the direction or management of such entity, whether by 19 | contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the 20 | outstanding shares, or (iii) beneficial ownership of such entity. 21 | 22 | "You" (or "Your") shall mean an individual or Legal Entity exercising 23 | permissions granted by this License. 24 | 25 | "Source" form shall mean the preferred form for making modifications, including 26 | but not limited to software source code, documentation source, and configuration 27 | files. 28 | 29 | "Object" form shall mean any form resulting from mechanical transformation or 30 | translation of a Source form, including but not limited to compiled object code, 31 | generated documentation, and conversions to other media types. 32 | 33 | "Work" shall mean the work of authorship, whether in Source or Object form, made 34 | available under the License, as indicated by a copyright notice that is included 35 | in or attached to the work (an example is provided in the Appendix below). 36 | 37 | "Derivative Works" shall mean any work, whether in Source or Object form, that 38 | is based on (or derived from) the Work and for which the editorial revisions, 39 | annotations, elaborations, or other modifications represent, as a whole, an 40 | original work of authorship. For the purposes of this License, Derivative Works 41 | shall not include works that remain separable from, or merely link (or bind by 42 | name) to the interfaces of, the Work and Derivative Works thereof. 43 | 44 | "Contribution" shall mean any work of authorship, including the original version 45 | of the Work and any modifications or additions to that Work or Derivative Works 46 | thereof, that is intentionally submitted to Licensor for inclusion in the Work 47 | by the copyright owner or by an individual or Legal Entity authorized to submit 48 | on behalf of the copyright owner. For the purposes of this definition, 49 | "submitted" means any form of electronic, verbal, or written communication sent 50 | to the Licensor or its representatives, including but not limited to 51 | communication on electronic mailing lists, source code control systems, and 52 | issue tracking systems that are managed by, or on behalf of, the Licensor for 53 | the purpose of discussing and improving the Work, but excluding communication 54 | that is conspicuously marked or otherwise designated in writing by the copyright 55 | owner as "Not a Contribution." 56 | 57 | "Contributor" shall mean Licensor and any individual or Legal Entity on behalf 58 | of whom a Contribution has been received by Licensor and subsequently 59 | incorporated within the Work. 60 | 61 | 2. Grant of Copyright License. 62 | 63 | Subject to the terms and conditions of this License, each Contributor hereby 64 | grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, 65 | irrevocable copyright license to reproduce, prepare Derivative Works of, 66 | publicly display, publicly perform, sublicense, and distribute the Work and such 67 | Derivative Works in Source or Object form. 68 | 69 | 3. Grant of Patent License. 70 | 71 | Subject to the terms and conditions of this License, each Contributor hereby 72 | grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, 73 | irrevocable (except as stated in this section) patent license to make, have 74 | made, use, offer to sell, sell, import, and otherwise transfer the Work, where 75 | such license applies only to those patent claims licensable by such Contributor 76 | that are necessarily infringed by their Contribution(s) alone or by combination 77 | of their Contribution(s) with the Work to which such Contribution(s) was 78 | submitted. If You institute patent litigation against any entity (including a 79 | cross-claim or counterclaim in a lawsuit) alleging that the Work or a 80 | Contribution incorporated within the Work constitutes direct or contributory 81 | patent infringement, then any patent licenses granted to You under this License 82 | for that Work shall terminate as of the date such litigation is filed. 83 | 84 | 4. Redistribution. 85 | 86 | You may reproduce and distribute copies of the Work or Derivative Works thereof 87 | in any medium, with or without modifications, and in Source or Object form, 88 | provided that You meet the following conditions: 89 | 90 | You must give any other recipients of the Work or Derivative Works a copy of 91 | this License; and 92 | You must cause any modified files to carry prominent notices stating that You 93 | changed the files; and 94 | You must retain, in the Source form of any Derivative Works that You distribute, 95 | all copyright, patent, trademark, and attribution notices from the Source form 96 | of the Work, excluding those notices that do not pertain to any part of the 97 | Derivative Works; and 98 | If the Work includes a "NOTICE" text file as part of its distribution, then any 99 | Derivative Works that You distribute must include a readable copy of the 100 | attribution notices contained within such NOTICE file, excluding those notices 101 | that do not pertain to any part of the Derivative Works, in at least one of the 102 | following places: within a NOTICE text file distributed as part of the 103 | Derivative Works; within the Source form or documentation, if provided along 104 | with the Derivative Works; or, within a display generated by the Derivative 105 | Works, if and wherever such third-party notices normally appear. The contents of 106 | the NOTICE file are for informational purposes only and do not modify the 107 | License. You may add Your own attribution notices within Derivative Works that 108 | You distribute, alongside or as an addendum to the NOTICE text from the Work, 109 | provided that such additional attribution notices cannot be construed as 110 | modifying the License. 111 | You may add Your own copyright statement to Your modifications and may provide 112 | additional or different license terms and conditions for use, reproduction, or 113 | distribution of Your modifications, or for any such Derivative Works as a whole, 114 | provided Your use, reproduction, and distribution of the Work otherwise complies 115 | with the conditions stated in this License. 116 | 117 | 5. Submission of Contributions. 118 | 119 | Unless You explicitly state otherwise, any Contribution intentionally submitted 120 | for inclusion in the Work by You to the Licensor shall be under the terms and 121 | conditions of this License, without any additional terms or conditions. 122 | Notwithstanding the above, nothing herein shall supersede or modify the terms of 123 | any separate license agreement you may have executed with Licensor regarding 124 | such Contributions. 125 | 126 | 6. Trademarks. 127 | 128 | This License does not grant permission to use the trade names, trademarks, 129 | service marks, or product names of the Licensor, except as required for 130 | reasonable and customary use in describing the origin of the Work and 131 | reproducing the content of the NOTICE file. 132 | 133 | 7. Disclaimer of Warranty. 134 | 135 | Unless required by applicable law or agreed to in writing, Licensor provides the 136 | Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, 137 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, 138 | including, without limitation, any warranties or conditions of TITLE, 139 | NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are 140 | solely responsible for determining the appropriateness of using or 141 | redistributing the Work and assume any risks associated with Your exercise of 142 | permissions under this License. 143 | 144 | 8. Limitation of Liability. 145 | 146 | In no event and under no legal theory, whether in tort (including negligence), 147 | contract, or otherwise, unless required by applicable law (such as deliberate 148 | and grossly negligent acts) or agreed to in writing, shall any Contributor be 149 | liable to You for damages, including any direct, indirect, special, incidental, 150 | or consequential damages of any character arising as a result of this License or 151 | out of the use or inability to use the Work (including but not limited to 152 | damages for loss of goodwill, work stoppage, computer failure or malfunction, or 153 | any and all other commercial damages or losses), even if such Contributor has 154 | been advised of the possibility of such damages. 155 | 156 | 9. Accepting Warranty or Additional Liability. 157 | 158 | While redistributing the Work or Derivative Works thereof, You may choose to 159 | offer, and charge a fee for, acceptance of support, warranty, indemnity, or 160 | other liability obligations and/or rights consistent with this License. However, 161 | in accepting such obligations, You may act only on Your own behalf and on Your 162 | sole responsibility, not on behalf of any other Contributor, and only if You 163 | agree to indemnify, defend, and hold each Contributor harmless for any liability 164 | incurred by, or claims asserted against, such Contributor by reason of your 165 | accepting any such warranty or additional liability. 166 | 167 | END OF TERMS AND CONDITIONS 168 | 169 | APPENDIX: How to apply the Apache License to your work 170 | 171 | To apply the Apache License to your work, attach the following boilerplate 172 | notice, with the fields enclosed by brackets "[]" replaced with your own 173 | identifying information. (Don't include the brackets!) The text should be 174 | enclosed in the appropriate comment syntax for the file format. We also 175 | recommend that a file or class name and description of purpose be included on 176 | the same "printed page" as the copyright notice for easier identification within 177 | third-party archives. 178 | 179 | Copyright [yyyy] [name of copyright owner] 180 | 181 | Licensed under the Apache License, Version 2.0 (the "License"); 182 | you may not use this file except in compliance with the License. 183 | You may obtain a copy of the License at 184 | 185 | http://www.apache.org/licenses/LICENSE-2.0 186 | 187 | Unless required by applicable law or agreed to in writing, software 188 | distributed under the License is distributed on an "AS IS" BASIS, 189 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 190 | See the License for the specific language governing permissions and 191 | limitations under the License. 192 | -------------------------------------------------------------------------------- /ch.bfh.swos.bookapp.vertx/src/main/webapp/resources/js/lib/angular-ui.min.js: -------------------------------------------------------------------------------- 1 | /** 2 | * AngularUI - The companion suite for AngularJS 3 | * @version v0.4.0 - 2013-02-15 4 | * @link http://angular-ui.github.com 5 | * @license MIT License, http://www.opensource.org/licenses/MIT 6 | */ 7 | angular.module("ui.config",[]).value("ui.config",{}),angular.module("ui.filters",["ui.config"]),angular.module("ui.directives",["ui.config"]),angular.module("ui",["ui.filters","ui.directives","ui.config"]),angular.module("ui.directives").directive("uiAnimate",["ui.config","$timeout",function(e,t){var n={};return angular.isString(e.animate)?n["class"]=e.animate:e.animate&&(n=e.animate),{restrict:"A",link:function(e,r,i){var s={};i.uiAnimate&&(s=e.$eval(i.uiAnimate),angular.isString(s)&&(s={"class":s})),s=angular.extend({"class":"ui-animate"},n,s),r.addClass(s["class"]),t(function(){r.removeClass(s["class"])},20,!1)}}}]),angular.module("ui.directives").directive("uiCalendar",["ui.config","$parse",function(e,t){return e.uiCalendar=e.uiCalendar||{},{require:"ngModel",restrict:"A",link:function(t,n,r,i){function a(){t.calendar=n.html("");var i=t.calendar.fullCalendar("getView");i&&(i=i.name);var o,u={defaultView:i,eventSources:s};r.uiCalendar?o=t.$eval(r.uiCalendar):o={},angular.extend(u,e.uiCalendar,o),t.calendar.fullCalendar(u)}var s=t.$eval(r.ngModel),o=0,u=function(){var e=t.$eval(r.equalsTracker);return o=0,angular.forEach(s,function(e,t){angular.isArray(e)&&(o+=e.length)}),angular.isNumber(e)?o+s.length+e:o+s.length};a(),t.$watch(u,function(e,t){a()})}}}]),angular.module("ui.directives").directive("uiCodemirror",["ui.config","$timeout",function(e,t){"use strict";var n=["cursorActivity","viewportChange","gutterClick","focus","blur","scroll","update"];return{restrict:"A",require:"ngModel",link:function(r,i,s,o){var u,a,f,l,c;if(i[0].type!=="textarea")throw new Error("uiCodemirror3 can only be applied to a textarea element");u=e.codemirror||{},a=angular.extend({},u,r.$eval(s.uiCodemirror)),f=function(e){return function(t,n){var i=t.getValue();i!==o.$viewValue&&(o.$setViewValue(i),r.$apply()),typeof e=="function"&&e(t,n)}},l=function(){c=CodeMirror.fromTextArea(i[0],a),c.on("change",f(a.onChange));for(var e=0,u=n.length,l;e0),r.toggleClass(o.neg,n<0),r.toggleClass(o.zero,n===0),e===""?r.text(""):r.text(t(n,o.symbol)),!0},s.$render=function(){a=s.$viewValue,r.val(a),u(a)}}}}]),angular.module("ui.directives").directive("uiDate",["ui.config",function(e){"use strict";var t;return t={},angular.isObject(e.date)&&angular.extend(t,e.date),{require:"?ngModel",link:function(t,n,r,i){var s=function(){return angular.extend({},e.date,t.$eval(r.uiDate))},o=function(){var e=s();if(i){var r=function(){t.$apply(function(){var e=n.datepicker("getDate");n.datepicker("setDate",n.val()),i.$setViewValue(e),n.blur()})};if(e.onSelect){var o=e.onSelect;e.onSelect=function(e,n){r(),t.$apply(function(){o(e,n)})}}else e.onSelect=r;n.bind("change",r),i.$render=function(){var e=i.$viewValue;if(angular.isDefined(e)&&e!==null&&!angular.isDate(e))throw new Error("ng-Model value must be a Date object - currently it is a "+typeof e+" - use ui-date-format to convert it from a string");n.datepicker("setDate",e)}}n.datepicker("destroy"),n.datepicker(e),i&&i.$render()};t.$watch(s,o,!0)}}}]).directive("uiDateFormat",["ui.config",function(e){var t={require:"ngModel",link:function(t,n,r,i){var s=r.uiDateFormat||e.dateFormat;s?(i.$formatters.push(function(e){if(angular.isString(e))return $.datepicker.parseDate(s,e)}),i.$parsers.push(function(e){if(e)return $.datepicker.formatDate(s,e)})):(i.$formatters.push(function(e){if(angular.isString(e))return new Date(e)}),i.$parsers.push(function(e){if(e)return e.toISOString()}))}};return t}]),angular.module("ui.directives").directive("uiEvent",["$parse",function(e){return function(t,n,r){var i=t.$eval(r.uiEvent);angular.forEach(i,function(r,i){var s=e(r);n.bind(i,function(e){var n=Array.prototype.slice.call(arguments);n=n.splice(1),t.$apply(function(){s(t,{$event:e,$params:n})})})})}}]),angular.module("ui.directives").directive("uiIf",[function(){return{transclude:"element",priority:1e3,terminal:!0,restrict:"A",compile:function(e,t,n){return function(e,t,r){var i,s;e.$watch(r.uiIf,function(r){i&&(i.remove(),i=undefined),s&&(s.$destroy(),s=undefined),r&&(s=e.$new(),n(s,function(e){i=e,t.after(e)}))})}}}}]),angular.module("ui.directives").directive("uiJq",["ui.config","$timeout",function(t,n){return{restrict:"A",compile:function(r,i){if(!angular.isFunction(r[i.uiJq]))throw new Error('ui-jq: The "'+i.uiJq+'" function does not exist');var s=t.jq&&t.jq[i.uiJq];return function(t,r,i){function u(){n(function(){r[i.uiJq].apply(r,o)},0,!1)}var o=[];i.uiOptions?(o=t.$eval("["+i.uiOptions+"]"),angular.isObject(s)&&angular.isObject(o[0])&&(o[0]=angular.extend({},s,o[0]))):s&&(o=[s]),i.ngModel&&r.is("select,input,textarea")&&r.on("change",function(){r.trigger("input")}),i.uiRefresh&&t.$watch(i.uiRefresh,function(e){u()}),u()}}}}]),angular.module("ui.directives").factory("keypressHelper",["$parse",function(t){var n={8:"backspace",9:"tab",13:"enter",27:"esc",32:"space",33:"pageup",34:"pagedown",35:"end",36:"home",37:"left",38:"up",39:"right",40:"down",45:"insert",46:"delete"},r=function(e){return e.charAt(0).toUpperCase()+e.slice(1)};return function(e,i,s,o){var u,a=[];u=i.$eval(o["ui"+r(e)]),angular.forEach(u,function(e,n){var r,i;i=t(e),angular.forEach(n.split(" "),function(e){r={expression:i,keys:{}},angular.forEach(e.split("-"),function(e){r.keys[e]=!0}),a.push(r)})}),s.bind(e,function(t){var r=t.metaKey||t.altKey,s=t.ctrlKey,o=t.shiftKey,u=t.keyCode;e==="keypress"&&!o&&u>=97&&u<=122&&(u-=32),angular.forEach(a,function(e){var u=e.keys[n[t.keyCode]]||e.keys[t.keyCode.toString()]||!1,a=e.keys.alt||!1,f=e.keys.ctrl||!1,l=e.keys.shift||!1;u&&a==r&&f==s&&l==o&&i.$apply(function(){e.expression(i,{$event:t})})})})}}]),angular.module("ui.directives").directive("uiKeydown",["keypressHelper",function(e){return{link:function(t,n,r){e("keydown",t,n,r)}}}]),angular.module("ui.directives").directive("uiKeypress",["keypressHelper",function(e){return{link:function(t,n,r){e("keypress",t,n,r)}}}]),angular.module("ui.directives").directive("uiKeyup",["keypressHelper",function(e){return{link:function(t,n,r){e("keyup",t,n,r)}}}]),function(){function t(e,t,n,r){angular.forEach(t.split(" "),function(t){var i={type:"map-"+t};google.maps.event.addListener(n,t,function(t){r.triggerHandler(angular.extend({},i,t)),e.$$phase||e.$apply()})})}function n(n,r){e.directive(n,[function(){return{restrict:"A",link:function(e,i,s){e.$watch(s[n],function(n){t(e,r,n,i)})}}}])}var e=angular.module("ui.directives");e.directive("uiMap",["ui.config","$parse",function(e,n){var r="bounds_changed center_changed click dblclick drag dragend dragstart heading_changed idle maptypeid_changed mousemove mouseout mouseover projection_changed resize rightclick tilesloaded tilt_changed zoom_changed",i=e.map||{};return{restrict:"A",link:function(e,s,o){var u=angular.extend({},i,e.$eval(o.uiOptions)),a=new google.maps.Map(s[0],u),f=n(o.uiMap);f.assign(e,a),t(e,r,a,s)}}}]),e.directive("uiMapInfoWindow",["ui.config","$parse","$compile",function(e,n,r){var i="closeclick content_change domready position_changed zindex_changed",s=e.mapInfoWindow||{};return{link:function(e,o,u){var a=angular.extend({},s,e.$eval(u.uiOptions));a.content=o[0];var f=n(u.uiMapInfoWindow),l=f(e);l||(l=new google.maps.InfoWindow(a),f.assign(e,l)),t(e,i,l,o),o.replaceWith("
");var c=l.open;l.open=function(n,i,s,u,a,f){r(o.contents())(e),c.call(l,n,i,s,u,a,f)}}}}]),n("uiMapMarker","animation_changed click clickable_changed cursor_changed dblclick drag dragend draggable_changed dragstart flat_changed icon_changed mousedown mouseout mouseover mouseup position_changed rightclick shadow_changed shape_changed title_changed visible_changed zindex_changed"),n("uiMapPolyline","click dblclick mousedown mousemove mouseout mouseover mouseup rightclick"),n("uiMapPolygon","click dblclick mousedown mousemove mouseout mouseover mouseup rightclick"),n("uiMapRectangle","bounds_changed click dblclick mousedown mousemove mouseout mouseover mouseup rightclick"),n("uiMapCircle","center_changed click dblclick mousedown mousemove mouseout mouseover mouseup radius_changed rightclick"),n("uiMapGroundOverlay","click dblclick")}(),angular.module("ui.directives").directive("uiMask",[function(){return{require:"ngModel",link:function(e,t,n,r){r.$render=function(){var i=r.$viewValue||"";t.val(i),t.mask(e.$eval(n.uiMask))},r.$parsers.push(function(e){var n=t.isMaskValid()||angular.isUndefined(t.isMaskValid())&&t.val().length>0;return r.$setValidity("mask",n),n?e:undefined}),t.bind("keyup",function(){e.$apply(function(){r.$setViewValue(t.mask())})})}}}]),angular.module("ui.directives").directive("uiReset",["ui.config",function(e){var t=null;return e.reset!==undefined&&(t=e.reset),{require:"ngModel",link:function(e,n,r,i){var s;s=angular.element(''),n.wrap('').after(s),s.bind("click",function(n){n.preventDefault(),e.$apply(function(){r.uiReset?i.$setViewValue(e.$eval(r.uiReset)):i.$setViewValue(t),i.$render()})})}}}]),angular.module("ui.directives").directive("uiRoute",["$location","$parse",function(e,t){return{restrict:"AC",compile:function(n,r){var i;if(r.uiRoute)i="uiRoute";else if(r.ngHref)i="ngHref";else{if(!r.href)throw new Error("uiRoute missing a route or href property on "+n[0]);i="href"}return function(n,r,s){function a(t){(hash=t.indexOf("#"))>-1&&(t=t.substr(hash+1)),u=function(){o(n,e.path().indexOf(t)>-1)},u()}function f(t){(hash=t.indexOf("#"))>-1&&(t=t.substr(hash+1)),u=function(){var i=new RegExp("^"+t+"$",["i"]);o(n,i.test(e.path()))},u()}var o=t(s.ngModel||s.routeModel||"$uiRoute").assign,u=angular.noop;switch(i){case"uiRoute":s.uiRoute?f(s.uiRoute):s.$observe("uiRoute",f);break;case"ngHref":s.ngHref?a(s.ngHref):s.$observe("ngHref",a);break;case"href":a(s.href)}n.$on("$routeChangeSuccess",function(){u()})}}}}]),angular.module("ui.directives").directive("uiScrollfix",["$window",function(e){"use strict";return{link:function(t,n,r){var i=n.offset().top;r.uiScrollfix?r.uiScrollfix.charAt(0)==="-"?r.uiScrollfix=i-r.uiScrollfix.substr(1):r.uiScrollfix.charAt(0)==="+"&&(r.uiScrollfix=i+parseFloat(r.uiScrollfix.substr(1))):r.uiScrollfix=i,angular.element(e).on("scroll.ui-scrollfix",function(){var t;if(angular.isDefined(e.pageYOffset))t=e.pageYOffset;else{var i=document.compatMode&&document.compatMode!=="BackCompat"?document.documentElement:document.body;t=i.scrollTop}!n.hasClass("ui-scrollfix")&&t>r.uiScrollfix?n.addClass("ui-scrollfix"):n.hasClass("ui-scrollfix")&&t'+t+""):e.replace(new RegExp(t,"gi"),'$&')):e}}),angular.module("ui.filters").filter("inflector",function(){function e(e){return e.replace(/^([a-z])|\s+([a-z])/g,function(e){return e.toUpperCase()})}function t(e,t){return e.replace(/[A-Z]/g,function(e){return t+e})}var n={humanize:function(n){return e(t(n," ").split("_").join(" "))},underscore:function(e){return e.substr(0,1).toLowerCase()+t(e.substr(1),"_").toLowerCase().split(" ").join("_")},variable:function(t){return t=t.substr(0,1).toLowerCase()+e(t.split("_").join(" ")).substr(1).split(" ").join(""),t}};return function(e,t,r){return t!==!1&&angular.isString(e)?(t=t||"humanize",n[t](e)):e}}),angular.module("ui.filters").filter("unique",function(){return function(e,t){if(t===!1)return e;if((t||angular.isUndefined(t))&&angular.isArray(e)){var n={},r=[],i=function(e){return angular.isObject(e)&&angular.isString(t)?e[t]:e};angular.forEach(e,function(e){var t,n=!1;for(var s=0;sthis.$items.length-1||t<0)return;return this.sliding?this.$element.one("slid",function(){r.to(t)}):n==t?this.pause().cycle():this.slide(t>n?"next":"prev",e(this.$items[t]))},pause:function(t){return t||(this.paused=!0),this.$element.find(".next, .prev").length&&e.support.transition.end&&(this.$element.trigger(e.support.transition.end),this.cycle(!0)),clearInterval(this.interval),this.interval=null,this},next:function(){if(this.sliding)return;return this.slide("next")},prev:function(){if(this.sliding)return;return this.slide("prev")},slide:function(t,n){var r=this.$element.find(".item.active"),i=n||r[t](),s=this.interval,o=t=="next"?"left":"right",u=t=="next"?"first":"last",a=this,f;this.sliding=!0,s&&this.pause(),i=i.length?i:this.$element.find(".item")[u](),f=e.Event("slide",{relatedTarget:i[0],direction:o});if(i.hasClass("active"))return;this.$indicators.length&&(this.$indicators.find(".active").removeClass("active"),this.$element.one("slid",function(){var t=e(a.$indicators.children()[a.getActiveIndex()]);t&&t.addClass("active")}));if(e.support.transition&&this.$element.hasClass("slide")){this.$element.trigger(f);if(f.isDefaultPrevented())return;i.addClass(t),i[0].offsetWidth,r.addClass(o),i.addClass(o),this.$element.one(e.support.transition.end,function(){i.removeClass([t,o].join(" ")).addClass("active"),r.removeClass(["active",o].join(" ")),a.sliding=!1,setTimeout(function(){a.$element.trigger("slid")},0)})}else{this.$element.trigger(f);if(f.isDefaultPrevented())return;r.removeClass("active"),i.addClass("active"),this.sliding=!1,this.$element.trigger("slid")}return s&&this.cycle(),this}};var n=e.fn.carousel;e.fn.carousel=function(n){return this.each(function(){var r=e(this),i=r.data("carousel"),s=e.extend({},e.fn.carousel.defaults,typeof n=="object"&&n),o=typeof n=="string"?n:s.slide;i||r.data("carousel",i=new t(this,s)),typeof n=="number"?i.to(n):o?i[o]():s.interval&&i.pause().cycle()})},e.fn.carousel.defaults={interval:5e3,pause:"hover"},e.fn.carousel.Constructor=t,e.fn.carousel.noConflict=function(){return e.fn.carousel=n,this},e(document).on("click.carousel.data-api","[data-slide], [data-slide-to]",function(t){var n=e(this),r,i=e(n.attr("data-target")||(r=n.attr("href"))&&r.replace(/.*(?=#[^\s]+$)/,"")),s=e.extend({},i.data(),n.data()),o;i.carousel(s),(o=n.attr("data-slide-to"))&&i.data("carousel").pause().to(o).cycle(),t.preventDefault()})}(window.jQuery),!function(e){"use strict";var t=function(t,n){this.$element=e(t),this.options=e.extend({},e.fn.collapse.defaults,n),this.options.parent&&(this.$parent=e(this.options.parent)),this.options.toggle&&this.toggle()};t.prototype={constructor:t,dimension:function(){var e=this.$element.hasClass("width");return e?"width":"height"},show:function(){var t,n,r,i;if(this.transitioning||this.$element.hasClass("in"))return;t=this.dimension(),n=e.camelCase(["scroll",t].join("-")),r=this.$parent&&this.$parent.find("> .accordion-group > .in");if(r&&r.length){i=r.data("collapse");if(i&&i.transitioning)return;r.collapse("hide"),i||r.data("collapse",null)}this.$element[t](0),this.transition("addClass",e.Event("show"),"shown"),e.support.transition&&this.$element[t](this.$element[0][n])},hide:function(){var t;if(this.transitioning||!this.$element.hasClass("in"))return;t=this.dimension(),this.reset(this.$element[t]()),this.transition("removeClass",e.Event("hide"),"hidden"),this.$element[t](0)},reset:function(e){var t=this.dimension();return this.$element.removeClass("collapse")[t](e||"auto")[0].offsetWidth,this.$element[e!==null?"addClass":"removeClass"]("collapse"),this},transition:function(t,n,r){var i=this,s=function(){n.type=="show"&&i.reset(),i.transitioning=0,i.$element.trigger(r)};this.$element.trigger(n);if(n.isDefaultPrevented())return;this.transitioning=1,this.$element[t]("in"),e.support.transition&&this.$element.hasClass("collapse")?this.$element.one(e.support.transition.end,s):s()},toggle:function(){this[this.$element.hasClass("in")?"hide":"show"]()}};var n=e.fn.collapse;e.fn.collapse=function(n){return this.each(function(){var r=e(this),i=r.data("collapse"),s=e.extend({},e.fn.collapse.defaults,r.data(),typeof n=="object"&&n);i||r.data("collapse",i=new t(this,s)),typeof n=="string"&&i[n]()})},e.fn.collapse.defaults={toggle:!0},e.fn.collapse.Constructor=t,e.fn.collapse.noConflict=function(){return e.fn.collapse=n,this},e(document).on("click.collapse.data-api","[data-toggle=collapse]",function(t){var n=e(this),r,i=n.attr("data-target")||t.preventDefault()||(r=n.attr("href"))&&r.replace(/.*(?=#[^\s]+$)/,""),s=e(i).data("collapse")?"toggle":n.data();n[e(i).hasClass("in")?"addClass":"removeClass"]("collapsed"),e(i).collapse(s)})}(window.jQuery),!function(e){"use strict";function r(){e(t).each(function(){i(e(this)).removeClass("open")})}function i(t){var n=t.attr("data-target"),r;n||(n=t.attr("href"),n=n&&/#/.test(n)&&n.replace(/.*(?=#[^\s]*$)/,"")),r=n&&e(n);if(!r||!r.length)r=t.parent();return r}var t="[data-toggle=dropdown]",n=function(t){var n=e(t).on("click.dropdown.data-api",this.toggle);e("html").on("click.dropdown.data-api",function(){n.parent().removeClass("open")})};n.prototype={constructor:n,toggle:function(t){var n=e(this),s,o;if(n.is(".disabled, :disabled"))return;return s=i(n),o=s.hasClass("open"),r(),o||s.toggleClass("open"),n.focus(),!1},keydown:function(n){var r,s,o,u,a,f;if(!/(38|40|27)/.test(n.keyCode))return;r=e(this),n.preventDefault(),n.stopPropagation();if(r.is(".disabled, :disabled"))return;u=i(r),a=u.hasClass("open");if(!a||a&&n.keyCode==27)return n.which==27&&u.find(t).focus(),r.click();s=e("[role=menu] li:not(.divider):visible a",u);if(!s.length)return;f=s.index(s.filter(":focus")),n.keyCode==38&&f>0&&f--,n.keyCode==40&&f').appendTo(document.body),this.$backdrop.click(this.options.backdrop=="static"?e.proxy(this.$element[0].focus,this.$element[0]):e.proxy(this.hide,this)),i&&this.$backdrop[0].offsetWidth,this.$backdrop.addClass("in");if(!t)return;i?this.$backdrop.one(e.support.transition.end,t):t()}else!this.isShown&&this.$backdrop?(this.$backdrop.removeClass("in"),e.support.transition&&this.$element.hasClass("fade")?this.$backdrop.one(e.support.transition.end,t):t()):t&&t()}};var n=e.fn.modal;e.fn.modal=function(n){return this.each(function(){var r=e(this),i=r.data("modal"),s=e.extend({},e.fn.modal.defaults,r.data(),typeof n=="object"&&n);i||r.data("modal",i=new t(this,s)),typeof n=="string"?i[n]():s.show&&i.show()})},e.fn.modal.defaults={backdrop:!0,keyboard:!0,show:!0},e.fn.modal.Constructor=t,e.fn.modal.noConflict=function(){return e.fn.modal=n,this},e(document).on("click.modal.data-api",'[data-toggle="modal"]',function(t){var n=e(this),r=n.attr("href"),i=e(n.attr("data-target")||r&&r.replace(/.*(?=#[^\s]+$)/,"")),s=i.data("modal")?"toggle":e.extend({remote:!/#/.test(r)&&r},i.data(),n.data());t.preventDefault(),i.modal(s).one("hide",function(){n.focus()})})}(window.jQuery),!function(e){"use strict";var t=function(e,t){this.init("tooltip",e,t)};t.prototype={constructor:t,init:function(t,n,r){var i,s,o,u,a;this.type=t,this.$element=e(n),this.options=this.getOptions(r),this.enabled=!0,o=this.options.trigger.split(" ");for(a=o.length;a--;)u=o[a],u=="click"?this.$element.on("click."+this.type,this.options.selector,e.proxy(this.toggle,this)):u!="manual"&&(i=u=="hover"?"mouseenter":"focus",s=u=="hover"?"mouseleave":"blur",this.$element.on(i+"."+this.type,this.options.selector,e.proxy(this.enter,this)),this.$element.on(s+"."+this.type,this.options.selector,e.proxy(this.leave,this)));this.options.selector?this._options=e.extend({},this.options,{trigger:"manual",selector:""}):this.fixTitle()},getOptions:function(t){return t=e.extend({},e.fn[this.type].defaults,this.$element.data(),t),t.delay&&typeof t.delay=="number"&&(t.delay={show:t.delay,hide:t.delay}),t},enter:function(t){var n=e.fn[this.type].defaults,r={},i;this._options&&e.each(this._options,function(e,t){n[e]!=t&&(r[e]=t)},this),i=e(t.currentTarget)[this.type](r).data(this.type);if(!i.options.delay||!i.options.delay.show)return i.show();clearTimeout(this.timeout),i.hoverState="in",this.timeout=setTimeout(function(){i.hoverState=="in"&&i.show()},i.options.delay.show)},leave:function(t){var n=e(t.currentTarget)[this.type](this._options).data(this.type);this.timeout&&clearTimeout(this.timeout);if(!n.options.delay||!n.options.delay.hide)return n.hide();n.hoverState="out",this.timeout=setTimeout(function(){n.hoverState=="out"&&n.hide()},n.options.delay.hide)},show:function(){var t,n,r,i,s,o,u=e.Event("show");if(this.hasContent()&&this.enabled){this.$element.trigger(u);if(u.isDefaultPrevented())return;t=this.tip(),this.setContent(),this.options.animation&&t.addClass("fade"),s=typeof this.options.placement=="function"?this.options.placement.call(this,t[0],this.$element[0]):this.options.placement,t.detach().css({top:0,left:0,display:"block"}),this.options.container?t.appendTo(this.options.container):t.insertAfter(this.$element),n=this.getPosition(),r=t[0].offsetWidth,i=t[0].offsetHeight;switch(s){case"bottom":o={top:n.top+n.height,left:n.left+n.width/2-r/2};break;case"top":o={top:n.top-i,left:n.left+n.width/2-r/2};break;case"left":o={top:n.top+n.height/2-i/2,left:n.left-r};break;case"right":o={top:n.top+n.height/2-i/2,left:n.left+n.width}}this.applyPlacement(o,s),this.$element.trigger("shown")}},applyPlacement:function(e,t){var n=this.tip(),r=n[0].offsetWidth,i=n[0].offsetHeight,s,o,u,a;n.offset(e).addClass(t).addClass("in"),s=n[0].offsetWidth,o=n[0].offsetHeight,t=="top"&&o!=i&&(e.top=e.top+i-o,a=!0),t=="bottom"||t=="top"?(u=0,e.left<0&&(u=e.left*-2,e.left=0,n.offset(e),s=n[0].offsetWidth,o=n[0].offsetHeight),this.replaceArrow(u-r+s,s,"left")):this.replaceArrow(o-i,o,"top"),a&&n.offset(e)},replaceArrow:function(e,t,n){this.arrow().css(n,e?50*(1-e/t)+"%":"")},setContent:function(){var e=this.tip(),t=this.getTitle();e.find(".tooltip-inner")[this.options.html?"html":"text"](t),e.removeClass("fade in top bottom left right")},hide:function(){function i(){var t=setTimeout(function(){n.off(e.support.transition.end).detach()},500);n.one(e.support.transition.end,function(){clearTimeout(t),n.detach()})}var t=this,n=this.tip(),r=e.Event("hide");this.$element.trigger(r);if(r.isDefaultPrevented())return;return n.removeClass("in"),e.support.transition&&this.$tip.hasClass("fade")?i():n.detach(),this.$element.trigger("hidden"),this},fixTitle:function(){var e=this.$element;(e.attr("title")||typeof e.attr("data-original-title")!="string")&&e.attr("data-original-title",e.attr("title")||"").attr("title","")},hasContent:function(){return this.getTitle()},getPosition:function(){var t=this.$element[0];return e.extend({},typeof t.getBoundingClientRect=="function"?t.getBoundingClientRect():{width:t.offsetWidth,height:t.offsetHeight},this.$element.offset())},getTitle:function(){var e,t=this.$element,n=this.options;return e=t.attr("data-original-title")||(typeof n.title=="function"?n.title.call(t[0]):n.title),e},tip:function(){return this.$tip=this.$tip||e(this.options.template)},arrow:function(){return this.$arrow=this.$arrow||this.tip().find(".tooltip-arrow")},validate:function(){this.$element[0].parentNode||(this.hide(),this.$element=null,this.options=null)},enable:function(){this.enabled=!0},disable:function(){this.enabled=!1},toggleEnabled:function(){this.enabled=!this.enabled},toggle:function(t){var n=t?e(t.currentTarget)[this.type](this._options).data(this.type):this;n.tip().hasClass("in")?n.hide():n.show()},destroy:function(){this.hide().$element.off("."+this.type).removeData(this.type)}};var n=e.fn.tooltip;e.fn.tooltip=function(n){return this.each(function(){var r=e(this),i=r.data("tooltip"),s=typeof n=="object"&&n;i||r.data("tooltip",i=new t(this,s)),typeof n=="string"&&i[n]()})},e.fn.tooltip.Constructor=t,e.fn.tooltip.defaults={animation:!0,placement:"top",selector:!1,template:'
',trigger:"hover focus",title:"",delay:0,html:!1,container:!1},e.fn.tooltip.noConflict=function(){return e.fn.tooltip=n,this}}(window.jQuery),!function(e){"use strict";var t=function(e,t){this.init("popover",e,t)};t.prototype=e.extend({},e.fn.tooltip.Constructor.prototype,{constructor:t,setContent:function(){var e=this.tip(),t=this.getTitle(),n=this.getContent();e.find(".popover-title")[this.options.html?"html":"text"](t),e.find(".popover-content")[this.options.html?"html":"text"](n),e.removeClass("fade top bottom left right in")},hasContent:function(){return this.getTitle()||this.getContent()},getContent:function(){var e,t=this.$element,n=this.options;return e=(typeof n.content=="function"?n.content.call(t[0]):n.content)||t.attr("data-content"),e},tip:function(){return this.$tip||(this.$tip=e(this.options.template)),this.$tip},destroy:function(){this.hide().$element.off("."+this.type).removeData(this.type)}});var n=e.fn.popover;e.fn.popover=function(n){return this.each(function(){var r=e(this),i=r.data("popover"),s=typeof n=="object"&&n;i||r.data("popover",i=new t(this,s)),typeof n=="string"&&i[n]()})},e.fn.popover.Constructor=t,e.fn.popover.defaults=e.extend({},e.fn.tooltip.defaults,{placement:"right",trigger:"click",content:"",template:'

'}),e.fn.popover.noConflict=function(){return e.fn.popover=n,this}}(window.jQuery),!function(e){"use strict";function t(t,n){var r=e.proxy(this.process,this),i=e(t).is("body")?e(window):e(t),s;this.options=e.extend({},e.fn.scrollspy.defaults,n),this.$scrollElement=i.on("scroll.scroll-spy.data-api",r),this.selector=(this.options.target||(s=e(t).attr("href"))&&s.replace(/.*(?=#[^\s]+$)/,"")||"")+" .nav li > a",this.$body=e("body"),this.refresh(),this.process()}t.prototype={constructor:t,refresh:function(){var t=this,n;this.offsets=e([]),this.targets=e([]),n=this.$body.find(this.selector).map(function(){var n=e(this),r=n.data("target")||n.attr("href"),i=/^#\w/.test(r)&&e(r);return i&&i.length&&[[i.position().top+(!e.isWindow(t.$scrollElement.get(0))&&t.$scrollElement.scrollTop()),r]]||null}).sort(function(e,t){return e[0]-t[0]}).each(function(){t.offsets.push(this[0]),t.targets.push(this[1])})},process:function(){var e=this.$scrollElement.scrollTop()+this.options.offset,t=this.$scrollElement[0].scrollHeight||this.$body[0].scrollHeight,n=t-this.$scrollElement.height(),r=this.offsets,i=this.targets,s=this.activeTarget,o;if(e>=n)return s!=(o=i.last()[0])&&this.activate(o);for(o=r.length;o--;)s!=i[o]&&e>=r[o]&&(!r[o+1]||e<=r[o+1])&&this.activate(i[o])},activate:function(t){var n,r;this.activeTarget=t,e(this.selector).parent(".active").removeClass("active"),r=this.selector+'[data-target="'+t+'"],'+this.selector+'[href="'+t+'"]',n=e(r).parent("li").addClass("active"),n.parent(".dropdown-menu").length&&(n=n.closest("li.dropdown").addClass("active")),n.trigger("activate")}};var n=e.fn.scrollspy;e.fn.scrollspy=function(n){return this.each(function(){var r=e(this),i=r.data("scrollspy"),s=typeof n=="object"&&n;i||r.data("scrollspy",i=new t(this,s)),typeof n=="string"&&i[n]()})},e.fn.scrollspy.Constructor=t,e.fn.scrollspy.defaults={offset:10},e.fn.scrollspy.noConflict=function(){return e.fn.scrollspy=n,this},e(window).on("load",function(){e('[data-spy="scroll"]').each(function(){var t=e(this);t.scrollspy(t.data())})})}(window.jQuery),!function(e){"use strict";var t=function(t){this.element=e(t)};t.prototype={constructor:t,show:function(){var t=this.element,n=t.closest("ul:not(.dropdown-menu)"),r=t.attr("data-target"),i,s,o;r||(r=t.attr("href"),r=r&&r.replace(/.*(?=#[^\s]*$)/,""));if(t.parent("li").hasClass("active"))return;i=n.find(".active:last a")[0],o=e.Event("show",{relatedTarget:i}),t.trigger(o);if(o.isDefaultPrevented())return;s=e(r),this.activate(t.parent("li"),n),this.activate(s,s.parent(),function(){t.trigger({type:"shown",relatedTarget:i})})},activate:function(t,n,r){function o(){i.removeClass("active").find("> .dropdown-menu > .active").removeClass("active"),t.addClass("active"),s?(t[0].offsetWidth,t.addClass("in")):t.removeClass("fade"),t.parent(".dropdown-menu")&&t.closest("li.dropdown").addClass("active"),r&&r()}var i=n.find("> .active"),s=r&&e.support.transition&&i.hasClass("fade");s?i.one(e.support.transition.end,o):o(),i.removeClass("in")}};var n=e.fn.tab;e.fn.tab=function(n){return this.each(function(){var r=e(this),i=r.data("tab");i||r.data("tab",i=new t(this)),typeof n=="string"&&i[n]()})},e.fn.tab.Constructor=t,e.fn.tab.noConflict=function(){return e.fn.tab=n,this},e(document).on("click.tab.data-api",'[data-toggle="tab"], [data-toggle="pill"]',function(t){t.preventDefault(),e(this).tab("show")})}(window.jQuery),!function(e){"use strict";var t=function(t,n){this.$element=e(t),this.options=e.extend({},e.fn.typeahead.defaults,n),this.matcher=this.options.matcher||this.matcher,this.sorter=this.options.sorter||this.sorter,this.highlighter=this.options.highlighter||this.highlighter,this.updater=this.options.updater||this.updater,this.source=this.options.source,this.$menu=e(this.options.menu),this.shown=!1,this.listen()};t.prototype={constructor:t,select:function(){var e=this.$menu.find(".active").attr("data-value");return this.$element.val(this.updater(e)).change(),this.hide()},updater:function(e){return e},show:function(){var t=e.extend({},this.$element.position(),{height:this.$element[0].offsetHeight});return this.$menu.insertAfter(this.$element).css({top:t.top+t.height,left:t.left}).show(),this.shown=!0,this},hide:function(){return this.$menu.hide(),this.shown=!1,this},lookup:function(t){var n;return this.query=this.$element.val(),!this.query||this.query.length"+t+""})},render:function(t){var n=this;return t=e(t).map(function(t,r){return t=e(n.options.item).attr("data-value",r),t.find("a").html(n.highlighter(r)),t[0]}),t.first().addClass("active"),this.$menu.html(t),this},next:function(t){var n=this.$menu.find(".active").removeClass("active"),r=n.next();r.length||(r=e(this.$menu.find("li")[0])),r.addClass("active")},prev:function(e){var t=this.$menu.find(".active").removeClass("active"),n=t.prev();n.length||(n=this.$menu.find("li").last()),n.addClass("active")},listen:function(){this.$element.on("focus",e.proxy(this.focus,this)).on("blur",e.proxy(this.blur,this)).on("keypress",e.proxy(this.keypress,this)).on("keyup",e.proxy(this.keyup,this)),this.eventSupported("keydown")&&this.$element.on("keydown",e.proxy(this.keydown,this)),this.$menu.on("click",e.proxy(this.click,this)).on("mouseenter","li",e.proxy(this.mouseenter,this)).on("mouseleave","li",e.proxy(this.mouseleave,this))},eventSupported:function(e){var t=e in this.$element;return t||(this.$element.setAttribute(e,"return;"),t=typeof this.$element[e]=="function"),t},move:function(e){if(!this.shown)return;switch(e.keyCode){case 9:case 13:case 27:e.preventDefault();break;case 38:e.preventDefault(),this.prev();break;case 40:e.preventDefault(),this.next()}e.stopPropagation()},keydown:function(t){this.suppressKeyPressRepeat=~e.inArray(t.keyCode,[40,38,9,13,27]),this.move(t)},keypress:function(e){if(this.suppressKeyPressRepeat)return;this.move(e)},keyup:function(e){switch(e.keyCode){case 40:case 38:case 16:case 17:case 18:break;case 9:case 13:if(!this.shown)return;this.select();break;case 27:if(!this.shown)return;this.hide();break;default:this.lookup()}e.stopPropagation(),e.preventDefault()},focus:function(e){this.focused=!0},blur:function(e){this.focused=!1,!this.mousedover&&this.shown&&this.hide()},click:function(e){e.stopPropagation(),e.preventDefault(),this.select(),this.$element.focus()},mouseenter:function(t){this.mousedover=!0,this.$menu.find(".active").removeClass("active"),e(t.currentTarget).addClass("active")},mouseleave:function(e){this.mousedover=!1,!this.focused&&this.shown&&this.hide()}};var n=e.fn.typeahead;e.fn.typeahead=function(n){return this.each(function(){var r=e(this),i=r.data("typeahead"),s=typeof n=="object"&&n;i||r.data("typeahead",i=new t(this,s)),typeof n=="string"&&i[n]()})},e.fn.typeahead.defaults={source:[],items:8,menu:'',item:'
  • ',minLength:1},e.fn.typeahead.Constructor=t,e.fn.typeahead.noConflict=function(){return e.fn.typeahead=n,this},e(document).on("focus.typeahead.data-api",'[data-provide="typeahead"]',function(t){var n=e(this);if(n.data("typeahead"))return;n.typeahead(n.data())})}(window.jQuery),!function(e){"use strict";var t=function(t,n){this.options=e.extend({},e.fn.affix.defaults,n),this.$window=e(window).on("scroll.affix.data-api",e.proxy(this.checkPosition,this)).on("click.affix.data-api",e.proxy(function(){setTimeout(e.proxy(this.checkPosition,this),1)},this)),this.$element=e(t),this.checkPosition()};t.prototype.checkPosition=function(){if(!this.$element.is(":visible"))return;var t=e(document).height(),n=this.$window.scrollTop(),r=this.$element.offset(),i=this.options.offset,s=i.bottom,o=i.top,u="affix affix-top affix-bottom",a;typeof i!="object"&&(s=o=i),typeof o=="function"&&(o=i.top()),typeof s=="function"&&(s=i.bottom()),a=this.unpin!=null&&n+this.unpin<=r.top?!1:s!=null&&r.top+this.$element.height()>=t-s?"bottom":o!=null&&n<=o?"top":!1;if(this.affixed===a)return;this.affixed=a,this.unpin=a=="bottom"?r.top-n:null,this.$element.removeClass(u).addClass("affix"+(a?"-"+a:""))};var n=e.fn.affix;e.fn.affix=function(n){return this.each(function(){var r=e(this),i=r.data("affix"),s=typeof n=="object"&&n;i||r.data("affix",i=new t(this,s)),typeof n=="string"&&i[n]()})},e.fn.affix.Constructor=t,e.fn.affix.defaults={offset:0},e.fn.affix.noConflict=function(){return e.fn.affix=n,this},e(window).on("load",function(){e('[data-spy="affix"]').each(function(){var t=e(this),n=t.data();n.offset=n.offset||{},n.offsetBottom&&(n.offset.bottom=n.offsetBottom),n.offsetTop&&(n.offset.top=n.offsetTop),t.affix(n)})})}(window.jQuery); --------------------------------------------------------------------------------