├── enterprise-granny-core ├── .gitignore ├── src │ ├── main │ │ └── java │ │ │ └── com │ │ │ └── sap │ │ │ └── hana │ │ │ └── cloud │ │ │ └── samples │ │ │ └── granny │ │ │ ├── model │ │ │ ├── package-info.java │ │ │ ├── Title.java │ │ │ ├── AddressType.java │ │ │ ├── CommunicationType.java │ │ │ ├── Salutation.java │ │ │ ├── PhoneNumber.java │ │ │ ├── EmailAddress.java │ │ │ ├── ValidationError.java │ │ │ ├── Address.java │ │ │ ├── Contact.java │ │ │ ├── StatusMessage.java │ │ │ └── BaseObject.java │ │ │ ├── srv │ │ │ ├── DataValidationException.java │ │ │ ├── ServiceException.java │ │ │ └── ContactService.java │ │ │ └── api │ │ │ └── ContactFacade.java │ └── test │ │ └── resources │ │ └── json │ │ └── donald.duck.json └── pom.xml ├── enterprise-granny-service ├── src │ ├── main │ │ ├── webapp │ │ │ ├── .gitignore │ │ │ ├── resources │ │ │ │ ├── css │ │ │ │ │ └── contact-srv.css │ │ │ │ └── img │ │ │ │ │ ├── favicon.ico │ │ │ │ │ ├── icon_9727.png │ │ │ │ │ ├── ensw_granny_logo_114.png │ │ │ │ │ ├── ensw_granny_logo_144.png │ │ │ │ │ ├── ensw_granny_logo_57.png │ │ │ │ │ ├── ensw_granny_logo_72.png │ │ │ │ │ ├── ensw_granny_logo_web.png │ │ │ │ │ └── icon_9727.svg │ │ │ ├── WEB-INF │ │ │ │ ├── lib │ │ │ │ │ └── .gitignore │ │ │ │ └── web.xml │ │ │ ├── META-INF │ │ │ │ └── MANIFEST.MF │ │ │ ├── index.html │ │ │ ├── css │ │ │ │ └── style.css │ │ │ └── admin │ │ │ │ └── index.html │ │ ├── resources │ │ │ ├── META-INF │ │ │ │ ├── enunciate │ │ │ │ │ ├── .gitignore │ │ │ │ │ ├── docs-base.zip │ │ │ │ │ └── enunciate.xml │ │ │ │ ├── persistence.xml │ │ │ │ └── spring │ │ │ │ │ └── spring-persistence-config.xml │ │ │ ├── sql │ │ │ │ └── dropSchema.sql │ │ │ ├── destinations │ │ │ │ └── destination.properties │ │ │ ├── db │ │ │ │ ├── db.changelog.xml │ │ │ │ └── multitenancy.changelog.xml │ │ │ ├── i18n │ │ │ │ └── messages.properties │ │ │ └── log4j.xml │ │ └── java │ │ │ └── com │ │ │ ├── sap │ │ │ └── hana │ │ │ │ └── cloud │ │ │ │ └── samples │ │ │ │ └── granny │ │ │ │ ├── api │ │ │ │ └── impl │ │ │ │ │ ├── package-info.java │ │ │ │ │ └── BaseFacade.java │ │ │ │ ├── dao │ │ │ │ ├── PhoneNumberValidationDAO.java │ │ │ │ ├── ContactDAO.java │ │ │ │ ├── PhoneNumberValidationResult.java │ │ │ │ ├── ContactRepository.java │ │ │ │ └── ContactDAOImpl.java │ │ │ │ ├── xcc │ │ │ │ ├── validations │ │ │ │ │ ├── ValidPhoneNumber.java │ │ │ │ │ └── ValidPhoneNumberValidator.java │ │ │ │ ├── LoggingAspect.java │ │ │ │ └── DataValidationAspect.java │ │ │ │ ├── srv │ │ │ │ ├── BaseService.java │ │ │ │ └── ContactServiceImpl.java │ │ │ │ ├── util │ │ │ │ ├── CustomObjectMapper.java │ │ │ │ └── LocaleUtils.java │ │ │ │ └── web │ │ │ │ └── util │ │ │ │ ├── ServiceExceptionMapper.java │ │ │ │ ├── ParserExceptionMapper.java │ │ │ │ ├── CustomJAXRSBeanValidationInInterceptor.java │ │ │ │ ├── JsonMappingExceptionMapper.java │ │ │ │ ├── EnvironmentContextInitializer.java │ │ │ │ ├── CustomJAXRSParameterNameProvider.java │ │ │ │ └── ValidationExceptionMapper.java │ │ │ └── osintegrators │ │ │ └── example │ │ │ ├── AddressRepository.java │ │ │ ├── AddressService.java │ │ │ ├── AddressServiceImpl.java │ │ │ ├── Address.java │ │ │ └── HomeController.java │ └── test │ │ ├── resources │ │ ├── com │ │ │ └── osintegrators │ │ │ │ └── example │ │ │ │ └── TestAddressService-context.xml │ │ └── log4j.xml │ │ └── java │ │ └── com │ │ ├── osintegrators │ │ └── example │ │ │ └── TestAddressService.java │ │ └── sap │ │ └── hana │ │ └── cloud │ │ └── samples │ │ └── granny │ │ └── dao │ │ ├── ValidPhoneNumberValidatorTest.java │ │ └── TestContactService.java ├── .gitignore └── manifest.yml ├── enterprise-granny-client ├── .gitignore ├── src │ └── main │ │ ├── webapp │ │ ├── WEB-INF │ │ │ ├── views │ │ │ │ ├── tiles │ │ │ │ │ ├── footer.jsp │ │ │ │ │ ├── js.jsp │ │ │ │ │ ├── meta.jsp │ │ │ │ │ ├── stylesheets.jsp │ │ │ │ │ └── navbar.jsp │ │ │ │ └── about.jsp │ │ │ ├── layouts │ │ │ │ ├── pjax.jsp │ │ │ │ └── default.jsp │ │ │ ├── tiles-defs.xml │ │ │ ├── web.xml │ │ │ ├── tags │ │ │ │ └── input.tag │ │ │ └── spring │ │ │ │ ├── root-context.xml │ │ │ │ └── appServlet │ │ │ │ └── servlet-context.xml │ │ └── resources │ │ │ ├── img │ │ │ ├── favicon.ico │ │ │ ├── icon_9727.png │ │ │ ├── ensw_granny_logo_57.png │ │ │ ├── ensw_granny_logo_72.png │ │ │ ├── ensw_granny_logo_114.png │ │ │ ├── ensw_granny_logo_144.png │ │ │ ├── ensw_granny_logo_web.png │ │ │ └── icon_9727.svg │ │ │ ├── js │ │ │ ├── xbreadcrumbs.js │ │ │ ├── html5shiv.js │ │ │ └── respond.min.js │ │ │ ├── fonts │ │ │ ├── glyphicons-halflings-regular.eot │ │ │ ├── glyphicons-halflings-regular.ttf │ │ │ └── glyphicons-halflings-regular.woff │ │ │ └── css │ │ │ ├── sticky-footer-navbar.css │ │ │ └── granny.css │ │ ├── resources │ │ ├── destinations │ │ │ └── destination.properties │ │ ├── i18n │ │ │ └── messages.properties │ │ └── log4j.xml │ │ └── java │ │ └── com │ │ └── sap │ │ └── hana │ │ └── cloud │ │ └── samples │ │ ├── granny │ │ └── client │ │ │ ├── web │ │ │ ├── formatter │ │ │ │ ├── TitleFormatter.java │ │ │ │ ├── SalutationFormatter.java │ │ │ │ └── CommunicationTypeFormatter.java │ │ │ ├── PJAXViewPreparer.java │ │ │ └── JPAXFilter.java │ │ │ └── AddressbookServiceFactory.java │ │ └── grannyv │ │ └── client │ │ └── web │ │ └── util │ │ ├── ServiceExceptionMapper.java │ │ ├── LocaleUtils.java │ │ └── ValidationExceptionMapper.java └── manifest.yml ├── enterprise-granny-phonelib └── src │ └── main │ ├── webapp │ ├── .gitignore │ ├── META-INF │ │ └── MANIFEST.MF │ ├── index.html │ └── WEB-INF │ │ ├── web.xml │ │ └── spring │ │ ├── spring-context.xml │ │ └── spring-context.xml.bak │ ├── resources │ └── META-INF │ │ └── enunciate │ │ ├── .gitignore │ │ ├── docs-base.zip │ │ └── enunciate.xml │ └── java │ └── com │ └── sap │ └── hana │ └── cloud │ └── samples │ └── granny │ └── libphonenumber │ ├── PhoneNumberValidationResult.java │ └── LibPhonenumberService.java ├── doc ├── 03_granny_pom.jpeg ├── 20a_12factor_app.jpg ├── 20a_DEV300_master.jpg ├── 20a_microservices.jpg ├── ensw_granny_logo_web.png ├── 08_ensw_granny8_after.png ├── 08_ensw_granny8_before.png ├── 20b_arch_blueprint_web.jpg ├── 09_granny_validation_postman.jpg ├── 05_enterprise-granny_domain_model.png ├── 09_programmable_web_growth_in_web_apis.jpg ├── 20a.md └── 01.md ├── .gitignore ├── CREDITS ├── LICENSE ├── diagrams └── enterprise-granny.xml └── pom.xml /enterprise-granny-core/.gitignore: -------------------------------------------------------------------------------- 1 | /target/ 2 | /.settings/ 3 | -------------------------------------------------------------------------------- /enterprise-granny-service/src/main/webapp/.gitignore: -------------------------------------------------------------------------------- 1 | /docs 2 | -------------------------------------------------------------------------------- /enterprise-granny-client/.gitignore: -------------------------------------------------------------------------------- 1 | /target/ 2 | /.settings/ 3 | -------------------------------------------------------------------------------- /enterprise-granny-phonelib/src/main/webapp/.gitignore: -------------------------------------------------------------------------------- 1 | /docs/ 2 | -------------------------------------------------------------------------------- /enterprise-granny-service/src/main/webapp/resources/css/contact-srv.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /enterprise-granny-service/.gitignore: -------------------------------------------------------------------------------- 1 | /target/ 2 | /.settings/ 3 | /derby.log 4 | -------------------------------------------------------------------------------- /enterprise-granny-service/src/main/webapp/WEB-INF/lib/.gitignore: -------------------------------------------------------------------------------- 1 | /ngdbc.jar 2 | -------------------------------------------------------------------------------- /enterprise-granny-phonelib/src/main/resources/META-INF/enunciate/.gitignore: -------------------------------------------------------------------------------- 1 | /docs-base 2 | -------------------------------------------------------------------------------- /enterprise-granny-service/src/main/resources/META-INF/enunciate/.gitignore: -------------------------------------------------------------------------------- 1 | /docs-base 2 | -------------------------------------------------------------------------------- /enterprise-granny-service/src/main/webapp/META-INF/MANIFEST.MF: -------------------------------------------------------------------------------- 1 | Manifest-Version: 1.0 2 | Class-Path: 3 | 4 | -------------------------------------------------------------------------------- /doc/03_granny_pom.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SAP-archive/cloud-enterprise-granny/HEAD/doc/03_granny_pom.jpeg -------------------------------------------------------------------------------- /enterprise-granny-client/src/main/webapp/WEB-INF/views/tiles/footer.jsp: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /enterprise-granny-phonelib/src/main/webapp/META-INF/MANIFEST.MF: -------------------------------------------------------------------------------- 1 | Manifest-Version: 1.0 2 | Class-Path: 3 | 4 | -------------------------------------------------------------------------------- /doc/20a_12factor_app.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SAP-archive/cloud-enterprise-granny/HEAD/doc/20a_12factor_app.jpg -------------------------------------------------------------------------------- /doc/20a_DEV300_master.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SAP-archive/cloud-enterprise-granny/HEAD/doc/20a_DEV300_master.jpg -------------------------------------------------------------------------------- /doc/20a_microservices.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SAP-archive/cloud-enterprise-granny/HEAD/doc/20a_microservices.jpg -------------------------------------------------------------------------------- /doc/ensw_granny_logo_web.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SAP-archive/cloud-enterprise-granny/HEAD/doc/ensw_granny_logo_web.png -------------------------------------------------------------------------------- /doc/08_ensw_granny8_after.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SAP-archive/cloud-enterprise-granny/HEAD/doc/08_ensw_granny8_after.png -------------------------------------------------------------------------------- /doc/08_ensw_granny8_before.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SAP-archive/cloud-enterprise-granny/HEAD/doc/08_ensw_granny8_before.png -------------------------------------------------------------------------------- /doc/20b_arch_blueprint_web.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SAP-archive/cloud-enterprise-granny/HEAD/doc/20b_arch_blueprint_web.jpg -------------------------------------------------------------------------------- /doc/09_granny_validation_postman.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SAP-archive/cloud-enterprise-granny/HEAD/doc/09_granny_validation_postman.jpg -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /bin 2 | /war 3 | *.DS_Store 4 | /target 5 | /build 6 | .project 7 | .classpath 8 | /.settings 9 | /derby.log 10 | *.sw[o|p] 11 | -------------------------------------------------------------------------------- /doc/05_enterprise-granny_domain_model.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SAP-archive/cloud-enterprise-granny/HEAD/doc/05_enterprise-granny_domain_model.png -------------------------------------------------------------------------------- /doc/09_programmable_web_growth_in_web_apis.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SAP-archive/cloud-enterprise-granny/HEAD/doc/09_programmable_web_growth_in_web_apis.jpg -------------------------------------------------------------------------------- /enterprise-granny-service/src/main/resources/sql/dropSchema.sql: -------------------------------------------------------------------------------- 1 | DROP TABLE "GRANNY_ADDRESS"; 2 | DROP TABLE "GRANNY_EMAIL"; 3 | DROP TABLE "GRANNY_PHONE"; 4 | DROP TABLE "GRANNY_CONTACT"; -------------------------------------------------------------------------------- /enterprise-granny-client/src/main/webapp/resources/img/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SAP-archive/cloud-enterprise-granny/HEAD/enterprise-granny-client/src/main/webapp/resources/img/favicon.ico -------------------------------------------------------------------------------- /enterprise-granny-client/src/main/webapp/resources/img/icon_9727.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SAP-archive/cloud-enterprise-granny/HEAD/enterprise-granny-client/src/main/webapp/resources/img/icon_9727.png -------------------------------------------------------------------------------- /enterprise-granny-service/src/main/webapp/resources/img/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SAP-archive/cloud-enterprise-granny/HEAD/enterprise-granny-service/src/main/webapp/resources/img/favicon.ico -------------------------------------------------------------------------------- /enterprise-granny-client/src/main/webapp/resources/js/xbreadcrumbs.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SAP-archive/cloud-enterprise-granny/HEAD/enterprise-granny-client/src/main/webapp/resources/js/xbreadcrumbs.js -------------------------------------------------------------------------------- /enterprise-granny-service/src/main/webapp/resources/img/icon_9727.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SAP-archive/cloud-enterprise-granny/HEAD/enterprise-granny-service/src/main/webapp/resources/img/icon_9727.png -------------------------------------------------------------------------------- /enterprise-granny-client/src/main/webapp/resources/img/ensw_granny_logo_57.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SAP-archive/cloud-enterprise-granny/HEAD/enterprise-granny-client/src/main/webapp/resources/img/ensw_granny_logo_57.png -------------------------------------------------------------------------------- /enterprise-granny-client/src/main/webapp/resources/img/ensw_granny_logo_72.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SAP-archive/cloud-enterprise-granny/HEAD/enterprise-granny-client/src/main/webapp/resources/img/ensw_granny_logo_72.png -------------------------------------------------------------------------------- /enterprise-granny-phonelib/src/main/resources/META-INF/enunciate/docs-base.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SAP-archive/cloud-enterprise-granny/HEAD/enterprise-granny-phonelib/src/main/resources/META-INF/enunciate/docs-base.zip -------------------------------------------------------------------------------- /enterprise-granny-service/src/main/resources/META-INF/enunciate/docs-base.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SAP-archive/cloud-enterprise-granny/HEAD/enterprise-granny-service/src/main/resources/META-INF/enunciate/docs-base.zip -------------------------------------------------------------------------------- /enterprise-granny-client/src/main/webapp/resources/img/ensw_granny_logo_114.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SAP-archive/cloud-enterprise-granny/HEAD/enterprise-granny-client/src/main/webapp/resources/img/ensw_granny_logo_114.png -------------------------------------------------------------------------------- /enterprise-granny-client/src/main/webapp/resources/img/ensw_granny_logo_144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SAP-archive/cloud-enterprise-granny/HEAD/enterprise-granny-client/src/main/webapp/resources/img/ensw_granny_logo_144.png -------------------------------------------------------------------------------- /enterprise-granny-client/src/main/webapp/resources/img/ensw_granny_logo_web.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SAP-archive/cloud-enterprise-granny/HEAD/enterprise-granny-client/src/main/webapp/resources/img/ensw_granny_logo_web.png -------------------------------------------------------------------------------- /enterprise-granny-service/src/main/webapp/resources/img/ensw_granny_logo_114.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SAP-archive/cloud-enterprise-granny/HEAD/enterprise-granny-service/src/main/webapp/resources/img/ensw_granny_logo_114.png -------------------------------------------------------------------------------- /enterprise-granny-service/src/main/webapp/resources/img/ensw_granny_logo_144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SAP-archive/cloud-enterprise-granny/HEAD/enterprise-granny-service/src/main/webapp/resources/img/ensw_granny_logo_144.png -------------------------------------------------------------------------------- /enterprise-granny-service/src/main/webapp/resources/img/ensw_granny_logo_57.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SAP-archive/cloud-enterprise-granny/HEAD/enterprise-granny-service/src/main/webapp/resources/img/ensw_granny_logo_57.png -------------------------------------------------------------------------------- /enterprise-granny-service/src/main/webapp/resources/img/ensw_granny_logo_72.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SAP-archive/cloud-enterprise-granny/HEAD/enterprise-granny-service/src/main/webapp/resources/img/ensw_granny_logo_72.png -------------------------------------------------------------------------------- /enterprise-granny-service/src/main/webapp/resources/img/ensw_granny_logo_web.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SAP-archive/cloud-enterprise-granny/HEAD/enterprise-granny-service/src/main/webapp/resources/img/ensw_granny_logo_web.png -------------------------------------------------------------------------------- /enterprise-granny-service/src/main/java/com/sap/hana/cloud/samples/granny/api/impl/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * We should really have something meaningful to say here... 3 | */ 4 | package com.sap.hana.cloud.samples.granny.api.impl; 5 | -------------------------------------------------------------------------------- /enterprise-granny-client/src/main/webapp/resources/fonts/glyphicons-halflings-regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SAP-archive/cloud-enterprise-granny/HEAD/enterprise-granny-client/src/main/webapp/resources/fonts/glyphicons-halflings-regular.eot -------------------------------------------------------------------------------- /enterprise-granny-client/src/main/webapp/resources/fonts/glyphicons-halflings-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SAP-archive/cloud-enterprise-granny/HEAD/enterprise-granny-client/src/main/webapp/resources/fonts/glyphicons-halflings-regular.ttf -------------------------------------------------------------------------------- /enterprise-granny-client/src/main/webapp/resources/fonts/glyphicons-halflings-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SAP-archive/cloud-enterprise-granny/HEAD/enterprise-granny-client/src/main/webapp/resources/fonts/glyphicons-halflings-regular.woff -------------------------------------------------------------------------------- /enterprise-granny-client/src/main/resources/destinations/destination.properties: -------------------------------------------------------------------------------- 1 | Name=Addressbook-Service 2 | URL=http\://localhost\:8080/api/v1 3 | ProxyType=Internet 4 | Type=HTTP 5 | Authentication=NoAuthentication 6 | Description=The URL address of the Adressbook-Service 7 | -------------------------------------------------------------------------------- /enterprise-granny-core/src/main/java/com/sap/hana/cloud/samples/granny/model/package-info.java: -------------------------------------------------------------------------------- 1 | @XmlSchema (namespace = "http://api.enterprise-granny.samples.cloud.sap.com/model") 2 | package com.sap.hana.cloud.samples.granny.model; 3 | 4 | import javax.xml.bind.annotation.XmlSchema; -------------------------------------------------------------------------------- /enterprise-granny-client/src/main/webapp/WEB-INF/layouts/pjax.jsp: -------------------------------------------------------------------------------- 1 | <%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%> 2 | <%@ taglib uri="http://tiles.apache.org/tags-tiles" prefix="tiles"%> 3 | 4 | 5 | -------------------------------------------------------------------------------- /enterprise-granny-phonelib/src/main/webapp/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Granny's Addressbook Service 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /enterprise-granny-service/src/main/java/com/sap/hana/cloud/samples/granny/api/impl/BaseFacade.java: -------------------------------------------------------------------------------- 1 | package com.sap.hana.cloud.samples.granny.api.impl; 2 | 3 | /** 4 | * Abstract base class for all facades. 5 | * 6 | */ 7 | public abstract class BaseFacade 8 | { 9 | 10 | } 11 | -------------------------------------------------------------------------------- /enterprise-granny-service/src/main/webapp/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Granny's Addressbook Service 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /enterprise-granny-service/manifest.yml: -------------------------------------------------------------------------------- 1 | --- 2 | applications: 3 | - name: enterprise-granny-${random-word} 4 | memory: 512M 5 | instances: 1 6 | host: enterprise-granny 7 | path: target/ROOT.war 8 | services: 9 | - hana 10 | env: 11 | SPRING_PROFILES_DEFAULT: cloud 12 | -------------------------------------------------------------------------------- /enterprise-granny-client/manifest.yml: -------------------------------------------------------------------------------- 1 | --- 2 | applications: 3 | - name: enterprise-granny-client-${random-word} 4 | memory: 512M 5 | instances: 1 6 | host: enterprise-granny-client 7 | path: target/client.war 8 | services: 9 | - hana 10 | env: 11 | SPRING_PROFILES_DEFAULT: cloud 12 | -------------------------------------------------------------------------------- /enterprise-granny-service/src/main/resources/destinations/destination.properties: -------------------------------------------------------------------------------- 1 | Name=PhoneNumber-Service 2 | URL=http\://localhost\:8080/phonelib/api/v1/phone?phonenumber={phonenumber}®ion={region} 3 | ProxyType=Internet 4 | Type=HTTP 5 | Authentication=NoAuthentication 6 | Description=The URL address of the PhoneNumber-Service 7 | -------------------------------------------------------------------------------- /enterprise-granny-service/src/main/resources/db/db.changelog.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /enterprise-granny-service/src/main/java/com/osintegrators/example/AddressRepository.java: -------------------------------------------------------------------------------- 1 | package com.osintegrators.example; 2 | 3 | import java.util.List; 4 | 5 | import org.springframework.data.repository.CrudRepository; 6 | 7 | /** 8 | * @deprecated please use {@link com.sap.hana.cloud.samples.granny.dao.ContactRepository} instead 9 | */ 10 | public interface AddressRepository extends CrudRepository { 11 | 12 | List
findAll(); 13 | 14 | } 15 | -------------------------------------------------------------------------------- /enterprise-granny-service/src/test/resources/com/osintegrators/example/TestAddressService-context.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | -------------------------------------------------------------------------------- /enterprise-granny-client/src/main/webapp/WEB-INF/views/tiles/js.jsp: -------------------------------------------------------------------------------- 1 | <%@ page language="java" contentType="text/html; charset=ISO-8859-1" %> 2 | <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /enterprise-granny-service/src/main/java/com/osintegrators/example/AddressService.java: -------------------------------------------------------------------------------- 1 | package com.osintegrators.example; 2 | 3 | import java.util.List; 4 | 5 | import com.osintegrators.example.Address; 6 | 7 | /** 8 | * @deprecated please use {@link com.sap.hana.cloud.samples.granny.srv.ContactService} instead 9 | */ 10 | public interface AddressService { 11 | 12 | void createAddress(Address add); 13 | 14 | void deleteAddress(Address add); 15 | 16 | List
getAllAddresses(); 17 | 18 | Address getAddressById(Long id); 19 | 20 | void updateAddress(Address address); 21 | 22 | } 23 | -------------------------------------------------------------------------------- /enterprise-granny-service/src/main/java/com/sap/hana/cloud/samples/granny/dao/PhoneNumberValidationDAO.java: -------------------------------------------------------------------------------- 1 | package com.sap.hana.cloud.samples.granny.dao; 2 | 3 | /** 4 | * Service for validating & formatting phone numbers. 5 | */ 6 | public interface PhoneNumberValidationDAO 7 | { 8 | /** 9 | * Validates the specified phone number taking into account the specified region code. 10 | * 11 | * @param phoneNumber The phone number to validate 12 | * @param region Region Code string using ISO 3166-1 two-letter country-code format in upper-case. 13 | * @return The validation result 14 | */ 15 | public PhoneNumberValidationResult validatePhoneNumber(String phoneNumber, String region); 16 | } 17 | -------------------------------------------------------------------------------- /enterprise-granny-core/src/test/resources/json/donald.duck.json: -------------------------------------------------------------------------------- 1 | { 2 | "salutation": "MR", 3 | "firstName": "Donald", 4 | "lastName": "Duck", 5 | "addresses": [ 6 | { 7 | "street": "3111 World Dr", 8 | "street2":"c/o Walt Disney World", 9 | "city": "Orlando", 10 | "zipCode": "32830", 11 | "country": "US" 12 | } 13 | ], 14 | "phoneNumbers": [ 15 | { 16 | "number": "+1 407 824-4321" 17 | } 18 | ], 19 | "emailAddresses": [ 20 | { 21 | "email": "donald.duck@disney.com" 22 | } 23 | ] 24 | } -------------------------------------------------------------------------------- /enterprise-granny-core/src/main/java/com/sap/hana/cloud/samples/granny/model/Title.java: -------------------------------------------------------------------------------- 1 | package com.sap.hana.cloud.samples.granny.model; 2 | 3 | /** 4 | * The title of a {@link Contact}. 5 | */ 6 | public enum Title 7 | { 8 | DR("model.title.dr"), 9 | PROF("model.title.prof"); 10 | 11 | /** 12 | * The I18N message key to be used. 13 | */ 14 | private final String key; 15 | 16 | /** 17 | * Creates a new {@link Title} entity with the specified I18N key. 18 | * 19 | * @param key The I18N message key to be used 20 | */ 21 | private Title(String key) 22 | { 23 | this.key = key; 24 | } 25 | 26 | /** 27 | * Returns the I18N key for this object. 28 | * 29 | * @return The I18N key for this object 30 | */ 31 | public String getKey() 32 | { 33 | return this.key; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /enterprise-granny-service/src/main/java/com/sap/hana/cloud/samples/granny/xcc/validations/ValidPhoneNumber.java: -------------------------------------------------------------------------------- 1 | package com.sap.hana.cloud.samples.granny.xcc.validations; 2 | 3 | import java.lang.annotation.Documented; 4 | import java.lang.annotation.ElementType; 5 | import java.lang.annotation.Retention; 6 | import java.lang.annotation.RetentionPolicy; 7 | import java.lang.annotation.Target; 8 | 9 | import javax.validation.Constraint; 10 | import javax.validation.Payload; 11 | 12 | @Target({ElementType.FIELD, ElementType.ANNOTATION_TYPE}) 13 | @Constraint(validatedBy = ValidPhoneNumberValidator.class) 14 | @Retention(RetentionPolicy.RUNTIME) 15 | @Documented 16 | public @interface ValidPhoneNumber 17 | { 18 | String message() default "phone_numer.number.validity.error"; 19 | 20 | Class[] groups() default {}; 21 | 22 | Class[] payload() default {}; 23 | } 24 | -------------------------------------------------------------------------------- /enterprise-granny-core/src/main/java/com/sap/hana/cloud/samples/granny/model/AddressType.java: -------------------------------------------------------------------------------- 1 | package com.sap.hana.cloud.samples.granny.model; 2 | 3 | /** 4 | * The type of the {@link Address}. 5 | */ 6 | public enum AddressType 7 | { 8 | PRIVATE("model.address_type.private"), 9 | WORK("model.address_type.work"); 10 | 11 | /** 12 | * The I18N message key to be used. 13 | */ 14 | private final String key; 15 | 16 | /** 17 | * Creates a new {@link AddressType} entity with the specified I18N key. 18 | * 19 | * @param key The I18N message key to be used 20 | */ 21 | private AddressType(String key) 22 | { 23 | this.key = key; 24 | } 25 | 26 | /** 27 | * Returns the I18N key for this object. 28 | * 29 | * @return The I18N key for this object 30 | */ 31 | public String getKey() 32 | { 33 | return this.key; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /enterprise-granny-client/src/main/webapp/resources/css/sticky-footer-navbar.css: -------------------------------------------------------------------------------- 1 | /* Sticky footer styles 2 | -------------------------------------------------- */ 3 | html { 4 | position: relative; 5 | min-height: 100%; 6 | } 7 | body { 8 | /* Margin bottom by footer height */ 9 | margin-bottom: 60px; 10 | } 11 | #footer { 12 | position: absolute; 13 | bottom: 0; 14 | width: 100%; 15 | /* Set the fixed height of the footer here */ 16 | height: 60px; 17 | background-color: #f5f5f5; 18 | } 19 | 20 | 21 | /* Custom page CSS 22 | -------------------------------------------------- */ 23 | /* Not required for template or sticky footer method. */ 24 | 25 | body > .container { 26 | padding: 60px 15px 0; 27 | } 28 | .container .text-muted { 29 | margin: 20px 0; 30 | } 31 | 32 | #footer > .container { 33 | padding-right: 15px; 34 | padding-left: 15px; 35 | } 36 | 37 | code { 38 | font-size: 80%; 39 | } 40 | -------------------------------------------------------------------------------- /enterprise-granny-client/src/main/webapp/WEB-INF/layouts/default.jsp: -------------------------------------------------------------------------------- 1 | <%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%> 2 | <%@ taglib uri="http://tiles.apache.org/tags-tiles" prefix="tiles"%> 3 | <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%> 4 | <%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt"%> 5 | 6 | 7 | 8 | <tiles:getAsString name="title" /> 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 |
18 | 19 |
20 | 21 |
22 | 23 | 24 |
25 | 26 | 27 | -------------------------------------------------------------------------------- /enterprise-granny-core/src/main/java/com/sap/hana/cloud/samples/granny/model/CommunicationType.java: -------------------------------------------------------------------------------- 1 | package com.sap.hana.cloud.samples.granny.model; 2 | 3 | /** 4 | * The communication type. 5 | */ 6 | public enum CommunicationType 7 | { 8 | HOME("model.communication_type.home"), 9 | WORK("model.communication_type.work"), 10 | CELL("model.communication_type.cell"); 11 | 12 | /** 13 | * The I18N message key to be used. 14 | */ 15 | private final String key; 16 | 17 | /** 18 | * Creates a new {@link CommunicationType} entity with the specified I18N key. 19 | * 20 | * @param key The I18N message key to be used 21 | */ 22 | private CommunicationType(String key) 23 | { 24 | this.key = key; 25 | } 26 | 27 | /** 28 | * Returns the I18N key for this object. 29 | * 30 | * @return The I18N key for this object 31 | */ 32 | public String getKey() 33 | { 34 | return this.key; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /enterprise-granny-client/src/main/webapp/WEB-INF/views/tiles/meta.jsp: -------------------------------------------------------------------------------- 1 | <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | "> 10 | "> 11 | "> 12 | "> 13 | "> 14 | 15 | -------------------------------------------------------------------------------- /enterprise-granny-client/src/main/java/com/sap/hana/cloud/samples/granny/client/web/formatter/TitleFormatter.java: -------------------------------------------------------------------------------- 1 | package com.sap.hana.cloud.samples.granny.client.web.formatter; 2 | 3 | import java.util.Locale; 4 | 5 | import javax.annotation.Resource; 6 | 7 | import org.springframework.context.MessageSource; 8 | import org.springframework.expression.ParseException; 9 | import org.springframework.format.Formatter; 10 | 11 | import com.sap.hana.cloud.samples.granny.model.Title; 12 | 13 | 14 | public class TitleFormatter implements Formatter 15 | { 16 | 17 | @Resource 18 | private MessageSource messageSource; 19 | 20 | @Override 21 | public String print(Title title, Locale locale) 22 | { 23 | return messageSource.getMessage(title.getKey(), null, title.name(), locale); 24 | } 25 | 26 | @Override 27 | public Title parse(String text, Locale locale) throws ParseException 28 | { 29 | return Title.valueOf(text.toUpperCase()); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /enterprise-granny-service/src/main/java/com/sap/hana/cloud/samples/granny/srv/BaseService.java: -------------------------------------------------------------------------------- 1 | package com.sap.hana.cloud.samples.granny.srv; 2 | 3 | 4 | /** 5 | * Abstract base class for all services. 6 | * 7 | */ 8 | public abstract class BaseService 9 | { 10 | /** 11 | * Creates an enclosing {@link ServiceException} for the specified {@link Exception} or 12 | * propagates it directly if the specified {@link Exception} is an instance of 13 | * {@link ServiceException}. 14 | * 15 | * @param ex The {@link Exception} to handle 16 | * @throws ServiceException The {@link ServiceException} 17 | */ 18 | protected void handleException(Exception ex) throws ServiceException 19 | { 20 | 21 | // final Logger logger = LoggerFactory.getLogger(this.getClass()); 22 | 23 | if (ex instanceof ServiceException) 24 | { 25 | throw (ServiceException) ex; 26 | } 27 | else 28 | { 29 | ServiceException up = new ServiceException(ex); 30 | throw up; 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /enterprise-granny-client/src/main/webapp/WEB-INF/views/tiles/stylesheets.jsp: -------------------------------------------------------------------------------- 1 | <%@ page language="java" contentType="text/html; charset=ISO-8859-1" %> 2 | <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> 3 | 4 | <!-- Latest compiled and minified CSS --> 5 | <link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min.css"> 6 | 7 | <!-- Optional theme --> 8 | <link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap-theme.min.css"> 9 | 10 | <!-- HTML5 shim and Respond.js IE8 support of HTML5 elements and media queries --> 11 | <!--[if lt IE 9]> 12 | <script src="<c:url value="/resources/js/html5shiv.js" />"></script> 13 | <script src="<c:url value="/resources/js/respond.min.js" />"></script> 14 | <![endif]--> 15 | 16 | <!-- Granny --> 17 | <link rel="stylesheet" href="<c:url value="/resources/css/granny.css" />" /> 18 | 19 | <!-- fonts --> 20 | <link href='http://fonts.googleapis.com/css?family=Pacifico' rel='stylesheet' type='text/css'> -------------------------------------------------------------------------------- /enterprise-granny-core/src/main/java/com/sap/hana/cloud/samples/granny/model/Salutation.java: -------------------------------------------------------------------------------- 1 | package com.sap.hana.cloud.samples.granny.model; 2 | 3 | /** 4 | * The salutation of a {@link Contact}. 5 | */ 6 | public enum Salutation 7 | { 8 | // Ladies first! ;) 9 | MS("model.salutation.ms"), 10 | MISSES("model.salutation.misses"), 11 | MRS("model.salutation.mrs"), 12 | MR("model.salutation.mr"), 13 | FAMILY("model.salutation.family"); 14 | 15 | /** 16 | * The I18N message key to be used. 17 | */ 18 | private final String key; 19 | 20 | /** 21 | * Creates a new {@link Salutation} entity with the specified I18N key. 22 | * 23 | * @param key The I18N message key to be used 24 | */ 25 | private Salutation(String key) 26 | { 27 | this.key = key; 28 | } 29 | 30 | /** 31 | * Returns the I18N key for this object. 32 | * 33 | * @return The I18N key for this object 34 | */ 35 | public String getKey() 36 | { 37 | return this.key; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /enterprise-granny-client/src/main/java/com/sap/hana/cloud/samples/granny/client/web/formatter/SalutationFormatter.java: -------------------------------------------------------------------------------- 1 | package com.sap.hana.cloud.samples.granny.client.web.formatter; 2 | 3 | import java.util.Locale; 4 | 5 | import javax.annotation.Resource; 6 | 7 | import org.springframework.context.MessageSource; 8 | import org.springframework.expression.ParseException; 9 | import org.springframework.format.Formatter; 10 | 11 | import com.sap.hana.cloud.samples.granny.model.Salutation; 12 | 13 | 14 | public class SalutationFormatter implements Formatter<Salutation> 15 | { 16 | 17 | @Resource 18 | private MessageSource messageSource; 19 | 20 | @Override 21 | public String print(Salutation salutation, Locale locale) 22 | { 23 | return messageSource.getMessage(salutation.getKey(), null, salutation.name(), locale); 24 | } 25 | 26 | @Override 27 | public Salutation parse(String text, Locale locale) throws ParseException 28 | { 29 | return Salutation.valueOf(text.toUpperCase()); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /enterprise-granny-service/src/main/java/com/sap/hana/cloud/samples/granny/dao/ContactDAO.java: -------------------------------------------------------------------------------- 1 | package com.sap.hana.cloud.samples.granny.dao; 2 | 3 | import java.util.List; 4 | 5 | import com.sap.hana.cloud.samples.granny.model.Contact; 6 | 7 | /** 8 | * Interface describing the life-cycle operations (e.g. CRUD operations) for {@link Contact} objects. 9 | */ 10 | public interface ContactDAO 11 | { 12 | 13 | public List<Contact> findAll(); 14 | 15 | public Contact save(Contact entity); 16 | 17 | public Iterable<Contact> save(Iterable<? extends Contact> entities); 18 | 19 | public Contact findOne(String id); 20 | 21 | 22 | public boolean exists(String id); 23 | 24 | 25 | public long count(); 26 | 27 | 28 | public void delete(String id); 29 | 30 | 31 | public void delete(Contact entity); 32 | 33 | 34 | public void delete(Iterable<? extends Contact> entities); 35 | 36 | 37 | public void deleteAll(); 38 | 39 | public List<Contact> findByAddressesCountry(String country); 40 | } 41 | -------------------------------------------------------------------------------- /enterprise-granny-service/src/main/resources/db/multitenancy.changelog.xml: -------------------------------------------------------------------------------- 1 | <?xml version="1.0" encoding="UTF-8"?> 2 | <databaseChangeLog 3 | xmlns="http://www.liquibase.org/xml/ns/dbchangelog" 4 | xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 5 | xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.4.xsd"> 6 | 7 | <!-- 8 | See: http://eclipse.org/eclipselink/documentation/2.5/jpa/extensions/a_multitenant.htm 9 | --> 10 | 11 | <changeSet id="Add discriminator column for multi-tenancy" author="Enterprise Granny"> 12 | <preConditions onFail="MARK_RAN" onFailMessage="Column 'TENANT_ID' already set! "> 13 | <not> 14 | <columnExists tableName="GRANNY_CONTACT" columnName="TENANT_ID"/> 15 | </not> 16 | </preConditions> 17 | <addColumn tableName="GRANNY_CONTACT"> 18 | <column name="TENANT_ID" type="varchar(36)" /> 19 | </addColumn> 20 | </changeSet> 21 | 22 | </databaseChangeLog> -------------------------------------------------------------------------------- /enterprise-granny-client/src/main/java/com/sap/hana/cloud/samples/granny/client/web/PJAXViewPreparer.java: -------------------------------------------------------------------------------- 1 | package com.sap.hana.cloud.samples.granny.client.web; 2 | 3 | import org.apache.tiles.Attribute; 4 | import org.apache.tiles.AttributeContext; 5 | import org.apache.tiles.context.TilesRequestContext; 6 | import org.apache.tiles.preparer.PreparerException; 7 | import org.apache.tiles.preparer.ViewPreparer; 8 | 9 | public class PJAXViewPreparer implements ViewPreparer 10 | { 11 | 12 | public void execute(TilesRequestContext tilesContext, AttributeContext attributeContext) throws PreparerException 13 | { 14 | if (tilesContext.getHeader().containsKey("x-pjax")) 15 | { 16 | Attribute template = attributeContext.getTemplateAttribute(); 17 | 18 | String templatePath = (String) template.getValue(); 19 | templatePath = templatePath.replace("default", "pjax"); 20 | 21 | template.setValue(templatePath); 22 | 23 | attributeContext.setTemplateAttribute(template); 24 | } 25 | } 26 | } -------------------------------------------------------------------------------- /enterprise-granny-client/src/main/resources/i18n/messages.properties: -------------------------------------------------------------------------------- 1 | model.address_type.private = Private 2 | model.address_type.work = Work 3 | 4 | model.communication_type.home = Home 5 | model.communication_type.work = Work 6 | model.communication_type.cell = Mobile 7 | 8 | model.salutation.ms = Ms. 9 | model.salutation.misses = Misses 10 | model.salutation.mrs = Mrs. 11 | model.salutation.mr = Mr. 12 | model.salutation.family = Family 13 | 14 | model.title.dr = Dr. 15 | model.title.prof = Prof. 16 | 17 | # generic domain model object attributes 18 | model.object.id.not_null.error = ID may not be NULL! 19 | 20 | # email 21 | model.email_address.email.pattern.error = This does not seem to be a valid email address! Typo? 22 | 23 | # phone number 24 | model.phone_numer.number.validity.error = Seems to be an invalid phone number! 25 | 26 | # validation messages 27 | phone_numer.number.validity.error = Seems to be an invalid phone number! 28 | 29 | api.data_validation.max_length.error = The maximum length is {max} characters! 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /enterprise-granny-service/src/main/java/com/sap/hana/cloud/samples/granny/dao/PhoneNumberValidationResult.java: -------------------------------------------------------------------------------- 1 | package com.sap.hana.cloud.samples.granny.dao; 2 | 3 | import java.io.Serializable; 4 | 5 | public class PhoneNumberValidationResult implements Serializable 6 | { 7 | 8 | private static final long serialVersionUID = 1L; 9 | 10 | boolean valid = false; 11 | 12 | String type = null; 13 | String number = null; 14 | String carrier = null; 15 | 16 | public boolean isValid() 17 | { 18 | return valid; 19 | } 20 | public void setValid(boolean valid) 21 | { 22 | this.valid = valid; 23 | } 24 | public String getType() 25 | { 26 | return type; 27 | } 28 | public void setType(String type) 29 | { 30 | this.type = type; 31 | } 32 | public String getNumber() 33 | { 34 | return number; 35 | } 36 | public void setNumber(String number) 37 | { 38 | this.number = number; 39 | } 40 | public String getCarrier() 41 | { 42 | return carrier; 43 | } 44 | public void setCarrier(String carrier) 45 | { 46 | this.carrier = carrier; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /enterprise-granny-service/src/main/resources/i18n/messages.properties: -------------------------------------------------------------------------------- 1 | model.address_type.private = Private 2 | model.address_type.work = Work 3 | 4 | model.communication_type.home = Home 5 | model.communication_type.work = Work 6 | model.communication_type.cell = Mobile 7 | 8 | model.salutation.ms = Ms. 9 | model.salutation.misses = Misses 10 | model.salutation.mrs = Mrs. 11 | model.salutation.mr = Mr. 12 | model.salutation.family = Family 13 | 14 | model.title.dr = Dr. 15 | model.title.prof = Prof. 16 | 17 | # generic domain model object attributes 18 | model.object.id.not_null.error = ID may not be NULL! 19 | 20 | # email 21 | model.email_address.email.pattern.error = This does not seem to be a valid email address! Typo? 22 | 23 | # phone number 24 | model.phone_numer.number.validity.error = Seems to be an invalid phone number! 25 | 26 | # validation messages 27 | phone_numer.number.validity.error = Seems to be an invalid phone number! 28 | 29 | api.data_validation.max_length.error = The maximum length is {max} characters! 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /enterprise-granny-client/src/main/java/com/sap/hana/cloud/samples/granny/client/web/formatter/CommunicationTypeFormatter.java: -------------------------------------------------------------------------------- 1 | package com.sap.hana.cloud.samples.granny.client.web.formatter; 2 | 3 | import java.util.Locale; 4 | 5 | import javax.annotation.Resource; 6 | 7 | import org.springframework.context.MessageSource; 8 | import org.springframework.expression.ParseException; 9 | import org.springframework.format.Formatter; 10 | 11 | import com.sap.hana.cloud.samples.granny.model.CommunicationType; 12 | 13 | public class CommunicationTypeFormatter implements Formatter<CommunicationType> 14 | { 15 | 16 | @Resource 17 | private MessageSource messageSource; 18 | 19 | @Override 20 | public String print(CommunicationType commType, Locale locale) 21 | { 22 | return messageSource.getMessage(commType.getKey(), null, commType.name(), locale); 23 | } 24 | 25 | @Override 26 | public CommunicationType parse(String text, Locale locale) throws ParseException 27 | { 28 | return CommunicationType.valueOf(text.toUpperCase()); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /enterprise-granny-phonelib/src/main/java/com/sap/hana/cloud/samples/granny/libphonenumber/PhoneNumberValidationResult.java: -------------------------------------------------------------------------------- 1 | package com.sap.hana.cloud.samples.granny.libphonenumber; 2 | 3 | import java.io.Serializable; 4 | 5 | public class PhoneNumberValidationResult implements Serializable 6 | { 7 | 8 | private static final long serialVersionUID = 1L; 9 | 10 | boolean valid = false; 11 | 12 | String type = null; 13 | String number = null; 14 | String carrier = null; 15 | 16 | public boolean isValid() 17 | { 18 | return valid; 19 | } 20 | public void setValid(boolean valid) 21 | { 22 | this.valid = valid; 23 | } 24 | public String getType() 25 | { 26 | return type; 27 | } 28 | public void setType(String type) 29 | { 30 | this.type = type; 31 | } 32 | public String getNumber() 33 | { 34 | return number; 35 | } 36 | public void setNumber(String number) 37 | { 38 | this.number = number; 39 | } 40 | public String getCarrier() 41 | { 42 | return carrier; 43 | } 44 | public void setCarrier(String carrier) 45 | { 46 | this.carrier = carrier; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /enterprise-granny-service/src/main/java/com/osintegrators/example/AddressServiceImpl.java: -------------------------------------------------------------------------------- 1 | package com.osintegrators.example; 2 | 3 | import java.util.List; 4 | 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.stereotype.Service; 7 | 8 | import com.osintegrators.example.Address; 9 | 10 | /** 11 | * @deprecated please use {@link com.sap.hana.cloud.samples.granny.srv.ContactServiceImpl} instead 12 | */ 13 | @Service 14 | public class AddressServiceImpl implements AddressService { 15 | 16 | @Autowired 17 | AddressRepository addressRepository; 18 | 19 | public void createAddress(Address address) { 20 | addressRepository.save(address); 21 | } 22 | 23 | public List<Address> getAllAddresses() { 24 | return addressRepository.findAll(); 25 | 26 | } 27 | 28 | public void deleteAddress(Address address) { 29 | addressRepository.delete(address); 30 | } 31 | 32 | public Address getAddressById(Long id) { 33 | return addressRepository.findOne(id); 34 | } 35 | 36 | public void updateAddress(Address address) { 37 | addressRepository.save(address); 38 | 39 | } 40 | 41 | } -------------------------------------------------------------------------------- /enterprise-granny-client/src/main/webapp/resources/css/granny.css: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | * navbar 4 | */ 5 | body 6 | { 7 | padding-top: 15px; 8 | } 9 | 10 | .navbar 11 | { 12 | margin-bottom: 15px; 13 | } 14 | 15 | @media (min-width: 768px) 16 | { 17 | body 18 | { 19 | padding-top: 30px; 20 | } 21 | 22 | .navbar 23 | { 24 | margin-bottom: 30px; 25 | } 26 | 27 | .navbar-nav>li>a 28 | { 29 | padding-top: 20px; 30 | padding-bottom: 15px; 31 | } 32 | 33 | } 34 | 35 | h1, 36 | .h1 { 37 | font-size: 20px; 38 | } 39 | 40 | h2, 41 | .h2 { 42 | font-size: 18px; 43 | } 44 | 45 | h3, 46 | .h3 { 47 | font-size: 16px; 48 | } 49 | 50 | h4, 51 | .h4 { 52 | font-size: 14px; 53 | } 54 | 55 | h5, 56 | .h5 { 57 | font-size: 12px; 58 | } 59 | 60 | h6, 61 | .h6 { 62 | font-size: 10px; 63 | } 64 | 65 | .navbar-brand 66 | { 67 | font-family: 'Pacifico', cursive; 68 | font-weight: 200; 69 | font-size: 200%; 70 | } 71 | 72 | @media(max-width:767px) 73 | { 74 | .navbar-brand 75 | { 76 | font-family: 'Pacifico', cursive; 77 | font-weight: 200; 78 | font-size: 150%; 79 | } 80 | } -------------------------------------------------------------------------------- /enterprise-granny-service/src/main/resources/META-INF/persistence.xml: -------------------------------------------------------------------------------- 1 | <?xml version="1.0" encoding="UTF-8"?> 2 | <persistence version="2.0" 3 | xmlns="http://java.sun.com/xml/ns/persistence" 4 | xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 5 | xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd"> 6 | 7 | <persistence-unit name="application" transaction-type="RESOURCE_LOCAL"> 8 | <provider>org.eclipse.persistence.jpa.PersistenceProvider</provider> 9 | 10 | <class>com.sap.hana.cloud.samples.granny.model.BaseObject</class> 11 | <class>com.sap.hana.cloud.samples.granny.model.Address</class> 12 | <class>com.sap.hana.cloud.samples.granny.model.Contact</class> 13 | <class>com.sap.hana.cloud.samples.granny.model.EmailAddress</class> 14 | <class>com.sap.hana.cloud.samples.granny.model.PhoneNumber</class> 15 | 16 | <exclude-unlisted-classes>true</exclude-unlisted-classes> 17 | <properties> 18 | <property name="eclipselink.ddl-generation" value="create-tables"/> 19 | </properties> 20 | </persistence-unit> 21 | 22 | </persistence> 23 | -------------------------------------------------------------------------------- /enterprise-granny-core/src/main/java/com/sap/hana/cloud/samples/granny/srv/DataValidationException.java: -------------------------------------------------------------------------------- 1 | package com.sap.hana.cloud.samples.granny.srv; 2 | 3 | import java.util.Arrays; 4 | 5 | import javax.validation.ConstraintViolationException; 6 | 7 | import com.sap.hana.cloud.samples.granny.model.StatusMessage; 8 | import com.sap.hana.cloud.samples.granny.model.ValidationError; 9 | import com.sap.hana.cloud.samples.granny.util.ConstraintViolationMapper; 10 | 11 | /** 12 | * {@link RuntimeException} used by the service layer. 13 | */ 14 | public class DataValidationException extends ServiceException 15 | { 16 | 17 | /** 18 | * The <code>serialVersionUID</code> of this class. 19 | */ 20 | private static final long serialVersionUID = 1L; 21 | 22 | public DataValidationException(ConstraintViolationException arg0) 23 | { 24 | super(arg0); 25 | this.msg = ConstraintViolationMapper.getDefaultStatusMessage(); 26 | } 27 | 28 | public DataValidationException(ValidationError... errors) 29 | { 30 | super(); 31 | this.msg = ConstraintViolationMapper.getDefaultStatusMessage(); 32 | this.msg.setErrors(Arrays.asList(errors)); 33 | } 34 | 35 | 36 | public StatusMessage getStatusMessage() 37 | { 38 | return this.msg; 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /enterprise-granny-service/src/test/resources/log4j.xml: -------------------------------------------------------------------------------- 1 | <?xml version="1.0" encoding="UTF-8"?> 2 | <!DOCTYPE log4j:configuration PUBLIC "-//APACHE//DTD LOG4J 1.2//EN" "log4j.dtd"> 3 | <log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/"> 4 | 5 | <!-- Appenders --> 6 | <appender name="console" class="org.apache.log4j.ConsoleAppender"> 7 | <param name="Target" value="System.out" /> 8 | <layout class="org.apache.log4j.PatternLayout"> 9 | <param name="ConversionPattern" value="%-5p: %c - %m%n" /> 10 | </layout> 11 | </appender> 12 | 13 | <!-- Application Loggers --> 14 | <logger name="com.osintegrators.example"> 15 | <level value="info" /> 16 | </logger> 17 | 18 | <!-- 3rdparty Loggers --> 19 | <logger name="org.springframework.core"> 20 | <level value="info" /> 21 | </logger> 22 | 23 | <logger name="org.springframework.beans"> 24 | <level value="info" /> 25 | </logger> 26 | 27 | <logger name="org.springframework.context"> 28 | <level value="info" /> 29 | </logger> 30 | 31 | <logger name="org.springframework.web"> 32 | <level value="info" /> 33 | </logger> 34 | 35 | <!-- Root Logger --> 36 | <root> 37 | <priority value="info" /> 38 | <appender-ref ref="console" /> 39 | </root> 40 | 41 | </log4j:configuration> 42 | -------------------------------------------------------------------------------- /enterprise-granny-client/src/main/webapp/WEB-INF/views/tiles/navbar.jsp: -------------------------------------------------------------------------------- 1 | <%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%> 2 | <%@ taglib prefix="tiles" uri="http://tiles.apache.org/tags-tiles" %> 3 | <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> 4 | 5 | <div class="navbar navbar-default"> 6 | <div class="navbar-header"> 7 | <button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse"> 8 | <span class="icon-bar"></span> 9 | <span class="icon-bar"></span> 10 | <span class="icon-bar"></span> 11 | </button> 12 | <a class="navbar-brand" href="<c:url value="/"/>" data-pjax><img src="<c:url value="/resources/img/icon_9727.png"/>" style="width: 40px; height: 40px; margin-top: -10px; margin-right: 10px;" class="hidden-xs hidden-sm"> <tiles:getAsString name="title" ignore="true" /></a> 13 | </div> 14 | <div class="navbar-collapse collapse"> 15 | <ul class="nav navbar-nav"> 16 | <li><a href="<c:url value="/about"/>" data-pjax>About</a></li> 17 | </ul> 18 | <ul class="nav navbar-nav navbar-right"> 19 | <li class="active"><a href="#"><%= System.getenv("HC_ACCOUNT") %></a></li> 20 | </ul> 21 | </div><!--/.nav-collapse --> 22 | </div> 23 | 24 | -------------------------------------------------------------------------------- /enterprise-granny-core/src/main/java/com/sap/hana/cloud/samples/granny/srv/ServiceException.java: -------------------------------------------------------------------------------- 1 | package com.sap.hana.cloud.samples.granny.srv; 2 | 3 | import com.sap.hana.cloud.samples.granny.model.StatusMessage; 4 | 5 | /** 6 | * {@link RuntimeException} used by the service layer. 7 | */ 8 | public class ServiceException extends RuntimeException 9 | { 10 | 11 | /** 12 | * The <code>serialVersionUID</code> of this class. 13 | */ 14 | private static final long serialVersionUID = 1L; 15 | 16 | StatusMessage msg = null; 17 | 18 | public ServiceException() 19 | { 20 | super(); 21 | msg = new StatusMessage(); 22 | } 23 | 24 | public ServiceException(String arg0) 25 | { 26 | super(arg0); 27 | msg = new StatusMessage(); 28 | msg.setMessage(arg0); 29 | } 30 | 31 | public ServiceException(Throwable arg0) 32 | { 33 | super(arg0); 34 | msg = new StatusMessage(); 35 | msg.setDescription(arg0.getMessage()); 36 | } 37 | 38 | public ServiceException(String arg0, Throwable arg1) 39 | { 40 | super(arg0, arg1); 41 | msg = new StatusMessage(); 42 | msg.setMessage(arg0); 43 | } 44 | 45 | public ServiceException(StatusMessage msg) 46 | { 47 | super(); 48 | this.msg = msg; 49 | } 50 | 51 | public StatusMessage getStatusMessage() 52 | { 53 | return this.msg; 54 | } 55 | 56 | } 57 | -------------------------------------------------------------------------------- /enterprise-granny-client/src/main/resources/log4j.xml: -------------------------------------------------------------------------------- 1 | <?xml version="1.0" encoding="UTF-8"?> 2 | <!DOCTYPE log4j:configuration PUBLIC "-//APACHE//DTD LOG4J 1.2//EN" "log4j.dtd"> 3 | <log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/"> 4 | 5 | <!-- Appenders --> 6 | <appender name="console" class="org.apache.log4j.ConsoleAppender"> 7 | <param name="Target" value="System.out" /> 8 | <layout class="org.apache.log4j.PatternLayout"> 9 | <param name="ConversionPattern" value="%-5p: %c - %m%n" /> 10 | </layout> 11 | </appender> 12 | 13 | <logger name="com.sap.hana.cloud.samples.granny"> 14 | <level value="all" /> 15 | </logger> 16 | 17 | <!-- 3rdparty Loggers --> 18 | <logger name="org.springframework.core"> 19 | <level value="info" /> 20 | </logger> 21 | 22 | <logger name="org.springframework.beans"> 23 | <level value="info" /> 24 | </logger> 25 | 26 | <logger name="org.springframework.context"> 27 | <level value="info" /> 28 | </logger> 29 | 30 | <logger name="org.springframework.web"> 31 | <level value="info" /> 32 | </logger> 33 | 34 | <logger name="com.sap.hana.cloud.samples.granny.client"> 35 | <level value="info" /> 36 | </logger> 37 | 38 | <!-- Root Logger --> 39 | <root> 40 | <priority value="warn" /> 41 | <appender-ref ref="console" /> 42 | </root> 43 | 44 | </log4j:configuration> 45 | -------------------------------------------------------------------------------- /enterprise-granny-core/src/main/java/com/sap/hana/cloud/samples/granny/model/PhoneNumber.java: -------------------------------------------------------------------------------- 1 | package com.sap.hana.cloud.samples.granny.model; 2 | 3 | import java.io.Serializable; 4 | 5 | import javax.validation.constraints.Size; 6 | 7 | import org.apache.commons.lang3.builder.ToStringBuilder; 8 | 9 | /** 10 | * The phone number of a {@link Contact}. 11 | */ 12 | public class PhoneNumber extends BaseObject implements Serializable 13 | { 14 | /** 15 | * The <code>serialVersionUID</code> of the class. 16 | */ 17 | private static final long serialVersionUID = 1L; 18 | 19 | protected CommunicationType type = null; 20 | 21 | @Size(max = 30, message = "{api.data_validation.max_length.error}") 22 | //@ValidPhoneNumber(message = "{model.phone_numer.number.validity.error}") 23 | protected String number = null; 24 | 25 | public CommunicationType getType() 26 | { 27 | return type; 28 | } 29 | 30 | public void setType(CommunicationType type) 31 | { 32 | this.type = type; 33 | } 34 | 35 | public String getNumber() 36 | { 37 | return number; 38 | } 39 | 40 | public void setNumber(String number) 41 | { 42 | this.number = number; 43 | } 44 | 45 | /** 46 | * @see java.lang.Object#toString() 47 | */ 48 | public String toString() 49 | { 50 | return new ToStringBuilder(this).appendSuper(super.toString()).append("type", this.type).append("number", this.number).toString(); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /enterprise-granny-service/src/main/java/com/sap/hana/cloud/samples/granny/util/CustomObjectMapper.java: -------------------------------------------------------------------------------- 1 | package com.sap.hana.cloud.samples.granny.util; 2 | 3 | import org.codehaus.jackson.map.AnnotationIntrospector; 4 | import org.codehaus.jackson.map.DeserializationConfig; 5 | import org.codehaus.jackson.map.ObjectMapper; 6 | import org.codehaus.jackson.map.SerializationConfig; 7 | import org.codehaus.jackson.map.annotate.JsonSerialize; 8 | import org.codehaus.jackson.xc.JaxbAnnotationIntrospector; 9 | 10 | /** 11 | * 12 | * 13 | * @see ObjectMapper 14 | */ 15 | public class CustomObjectMapper extends ObjectMapper 16 | { 17 | /** 18 | * {@inheritDoc} 19 | */ 20 | public CustomObjectMapper() 21 | { 22 | super(); 23 | this.configure(DeserializationConfig.Feature.FAIL_ON_UNKNOWN_PROPERTIES, false); 24 | this.configure(SerializationConfig.Feature.FAIL_ON_EMPTY_BEANS, false); 25 | this.configure(SerializationConfig.Feature.WRITE_DATES_AS_TIMESTAMPS, false); 26 | 27 | this.setSerializationInclusion(JsonSerialize.Inclusion.NON_DEFAULT); 28 | this.setSerializationInclusion(JsonSerialize.Inclusion.NON_NULL); 29 | this.setSerializationInclusion(JsonSerialize.Inclusion.NON_EMPTY); 30 | 31 | final AnnotationIntrospector introspector = new JaxbAnnotationIntrospector(); 32 | 33 | // make deserializer use JAXB annotations (only) 34 | this.setAnnotationIntrospector(introspector); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /enterprise-granny-phonelib/src/main/webapp/WEB-INF/web.xml: -------------------------------------------------------------------------------- 1 | <?xml version="1.0" encoding="UTF-8"?> 2 | <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 3 | xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee" 4 | xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" 5 | version="2.5"> 6 | 7 | <context-param> 8 | <param-name>contextConfigLocation</param-name> 9 | <param-value>/WEB-INF/spring/spring-context.xml</param-value> 10 | </context-param> 11 | <listener> 12 | <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> 13 | </listener> 14 | <servlet> 15 | <servlet-name>CXFServlet</servlet-name> 16 | <servlet-class>org.apache.cxf.transport.servlet.CXFServlet</servlet-class> 17 | <load-on-startup>1</load-on-startup> 18 | </servlet> 19 | <servlet-mapping> 20 | <servlet-name>CXFServlet</servlet-name> 21 | <url-pattern>/api/*</url-pattern> 22 | </servlet-mapping> 23 | <filter> 24 | <filter-name>RequestContextFilter</filter-name> 25 | <filter-class>org.springframework.web.filter.RequestContextFilter</filter-class> 26 | </filter> 27 | <filter-mapping> 28 | <filter-name>RequestContextFilter</filter-name> 29 | <url-pattern>/api/*</url-pattern> 30 | </filter-mapping> 31 | <welcome-file-list> 32 | <welcome-file>index.html</welcome-file> 33 | </welcome-file-list> 34 | 35 | </web-app> -------------------------------------------------------------------------------- /enterprise-granny-core/src/main/java/com/sap/hana/cloud/samples/granny/model/EmailAddress.java: -------------------------------------------------------------------------------- 1 | package com.sap.hana.cloud.samples.granny.model; 2 | 3 | import java.io.Serializable; 4 | 5 | import javax.validation.constraints.Size; 6 | 7 | import org.apache.commons.lang3.builder.ToStringBuilder; 8 | 9 | /** 10 | * EmailAddress address of a {@link Contact}. 11 | */ 12 | public class EmailAddress extends BaseObject implements Serializable 13 | { 14 | /** 15 | * The <code>serialVersionUID</code> of the class. 16 | */ 17 | private static final long serialVersionUID = 1L; 18 | 19 | protected AddressType type = null; 20 | 21 | @Size(max = 70, message = "{api.data_validation.max_length.error}") 22 | //@Pattern(regexp="^[\\w-\\.]+@([\\w-]+\\.)+[\\w-]{2,4}$", message = "{model.email_address.email.pattern.error}" ) 23 | protected String email = null; 24 | 25 | public AddressType getType() 26 | { 27 | return type; 28 | } 29 | 30 | public void setType(AddressType type) 31 | { 32 | this.type = type; 33 | } 34 | 35 | public String getEmail() 36 | { 37 | return email; 38 | } 39 | 40 | public void setEmail(String email) 41 | { 42 | this.email = email; 43 | } 44 | 45 | /** 46 | * @see java.lang.Object#toString() 47 | */ 48 | public String toString() 49 | { 50 | return new ToStringBuilder(this).appendSuper(super.toString()).append("type", this.type).append("email", this.email).toString(); 51 | } 52 | 53 | } 54 | -------------------------------------------------------------------------------- /enterprise-granny-client/src/main/java/com/sap/hana/cloud/samples/grannyv/client/web/util/ServiceExceptionMapper.java: -------------------------------------------------------------------------------- 1 | package com.sap.hana.cloud.samples.grannyv.client.web.util; 2 | 3 | import javax.inject.Inject; 4 | import javax.ws.rs.core.MediaType; 5 | import javax.ws.rs.core.Response; 6 | import javax.ws.rs.ext.ExceptionMapper; 7 | 8 | import org.codehaus.jackson.map.ObjectMapper; 9 | 10 | import com.sap.hana.cloud.samples.granny.model.StatusMessage; 11 | import com.sap.hana.cloud.samples.granny.srv.ServiceException; 12 | 13 | /** 14 | * 15 | * @see http://cxf.apache.org/docs/jax-rs-basics.html#JAX-RSBasics-Exceptionhandling 16 | * @see http://fusesource.com/docs/esb/4.2/rest/RESTExceptionMapper.html 17 | * 18 | */ 19 | public class ServiceExceptionMapper implements ExceptionMapper<ServiceException> 20 | { 21 | 22 | /** 23 | * The {@link ObjectMapper} to be used. 24 | * 25 | * @see CustomObjectMapper 26 | */ 27 | @Inject 28 | ObjectMapper objectMapper = null; 29 | 30 | 31 | /** 32 | * TODO 33 | * 34 | * @param exception The caught {@link ServiceException} 35 | * @return The corresponding {@link Response} containing a {@link StatusMessage} 36 | */ 37 | @Override 38 | public Response toResponse(ServiceException exception) 39 | { 40 | StatusMessage message = exception.getStatusMessage(); 41 | return Response.status(message.getCode()).entity(message).type(MediaType.APPLICATION_JSON).build(); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /enterprise-granny-service/src/main/java/com/sap/hana/cloud/samples/granny/dao/ContactRepository.java: -------------------------------------------------------------------------------- 1 | package com.sap.hana.cloud.samples.granny.dao; 2 | 3 | import java.util.List; 4 | 5 | import org.springframework.data.jpa.repository.Query; 6 | import org.springframework.data.repository.CrudRepository; 7 | 8 | import com.sap.hana.cloud.samples.granny.model.Address; 9 | import com.sap.hana.cloud.samples.granny.model.Contact; 10 | 11 | /** 12 | * Interface describing the life-cycle operations (e.g. CRUD operations) for {@link Contact} objects. 13 | */ 14 | public interface ContactRepository extends CrudRepository<Contact, String> 15 | { 16 | /** 17 | * Returns a {@link List} of all {@link Contact} objects. 18 | * 19 | * @return A {@link List} of all {@link Contact} objects 20 | */ 21 | @Query("SELECT DISTINCT c from Contact c JOIN FETCH c.addresses JOIN FETCH c.phoneNumbers JOIN FETCH c.emailAddresses ORDER BY c.lastName, c.firstName ASC") 22 | public List<Contact> queryAll(); 23 | 24 | /** 25 | * Returns a {@link List} of all {@link Contact} objects with an {@link Address} 26 | * matching the specified 2-letter country code. 27 | * 28 | * @param country The country to search for 29 | * @return {@link List} of all {@link Contact} objects with an {@link Address} matching the specified 2-letter country code 30 | * 31 | * @see http://www.davros.org/misc/iso3166.txt 32 | */ 33 | public List<Contact> findByAddressesCountry(String country); 34 | 35 | } 36 | -------------------------------------------------------------------------------- /enterprise-granny-service/src/main/java/com/sap/hana/cloud/samples/granny/web/util/ServiceExceptionMapper.java: -------------------------------------------------------------------------------- 1 | package com.sap.hana.cloud.samples.granny.web.util; 2 | 3 | import javax.inject.Inject; 4 | import javax.ws.rs.core.MediaType; 5 | import javax.ws.rs.core.Response; 6 | import javax.ws.rs.ext.ExceptionMapper; 7 | 8 | import org.codehaus.jackson.map.ObjectMapper; 9 | 10 | import com.sap.hana.cloud.samples.granny.model.StatusMessage; 11 | import com.sap.hana.cloud.samples.granny.srv.ServiceException; 12 | import com.sap.hana.cloud.samples.granny.util.CustomObjectMapper; 13 | 14 | /** 15 | * 16 | * @see http://cxf.apache.org/docs/jax-rs-basics.html#JAX-RSBasics-Exceptionhandling 17 | * @see http://fusesource.com/docs/esb/4.2/rest/RESTExceptionMapper.html 18 | * 19 | */ 20 | public class ServiceExceptionMapper implements ExceptionMapper<ServiceException> 21 | { 22 | 23 | /** 24 | * The {@link ObjectMapper} to be used. 25 | * 26 | * @see CustomObjectMapper 27 | */ 28 | @Inject 29 | ObjectMapper objectMapper = null; 30 | 31 | 32 | /** 33 | * TODO 34 | * 35 | * @param exception The caught {@link ServiceException} 36 | * @return The corresponding {@link Response} containing a {@link StatusMessage} 37 | */ 38 | @Override 39 | public Response toResponse(ServiceException exception) 40 | { 41 | StatusMessage message = exception.getStatusMessage(); 42 | return Response.status(message.getCode()).entity(message).type(MediaType.APPLICATION_JSON).build(); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /enterprise-granny-client/src/main/webapp/WEB-INF/tiles-defs.xml: -------------------------------------------------------------------------------- 1 | <?xml version="1.0" encoding="ISO-8859-1" ?> 2 | <!DOCTYPE tiles-definitions PUBLIC 3 | "-//Apache Software Foundation//DTD Tiles Configuration 2.1//EN" 4 | "http://tiles.apache.org/dtds/tiles-config_2_1.dtd"> 5 | <tiles-definitions> 6 | <definition name="default.definition" template="/WEB-INF/layouts/default.jsp"> 7 | <put-attribute name="meta" value="/WEB-INF/views/tiles/meta.jsp" /> 8 | <put-attribute name="js" value="/WEB-INF/views/tiles/js.jsp" /> 9 | <put-attribute name="stylesheets" value="/WEB-INF/views/tiles/stylesheets.jsp" /> 10 | <put-attribute name="navbar" value="/WEB-INF/views/tiles/navbar.jsp" /> 11 | <put-attribute name="content" value="" /> 12 | <put-attribute name="footer" value="/WEB-INF/views/tiles/footer.jsp" /> 13 | </definition> 14 | 15 | <definition name="contacts" extends="default.definition" preparer="com.sap.hana.cloud.samples.contactsrv.client.web.PJAXViewPreparer"> 16 | <put-attribute name="title" value="Granny's Addressbook" type="string" cascade="true"/> 17 | <put-attribute name="content" value="/WEB-INF/views/contact.jsp" /> 18 | </definition> 19 | 20 | <definition name="about" extends="default.definition" preparer="com.sap.hana.cloud.samples.contactsrv.client.web.PJAXViewPreparer"> 21 | <put-attribute name="title" value="Granny's Addressbook" type="string" cascade="true"/> 22 | <put-attribute name="content" value="/WEB-INF/views/about.jsp" /> 23 | </definition> 24 | 25 | </tiles-definitions> -------------------------------------------------------------------------------- /enterprise-granny-core/src/main/java/com/sap/hana/cloud/samples/granny/api/ContactFacade.java: -------------------------------------------------------------------------------- 1 | package com.sap.hana.cloud.samples.granny.api; 2 | 3 | 4 | import java.util.List; 5 | 6 | import javax.validation.Valid; 7 | import javax.ws.rs.Consumes; 8 | import javax.ws.rs.DELETE; 9 | import javax.ws.rs.GET; 10 | import javax.ws.rs.POST; 11 | import javax.ws.rs.PUT; 12 | import javax.ws.rs.Path; 13 | import javax.ws.rs.PathParam; 14 | import javax.ws.rs.Produces; 15 | import javax.ws.rs.core.MediaType; 16 | 17 | import com.sap.hana.cloud.samples.granny.model.Contact; 18 | import com.sap.hana.cloud.samples.granny.srv.ServiceException; 19 | 20 | @Path("/contacts") 21 | public interface ContactFacade 22 | { 23 | @Produces("application/json") 24 | @GET 25 | public List<Contact> findAll() throws ServiceException; 26 | 27 | @Path("/{id}") 28 | @Produces("application/json") 29 | @GET 30 | public Contact findOne(@PathParam("id") String id) throws ServiceException; 31 | 32 | @Consumes("application/json") 33 | @Produces("application/json") 34 | @POST 35 | public Contact create(@Valid Contact contact) throws ServiceException; 36 | 37 | @Path("/{id}") 38 | @Consumes("application/json") 39 | @Produces("application/json") 40 | @PUT 41 | public Contact update(@PathParam("id") String id, @Valid Contact contact) throws ServiceException; 42 | 43 | @Path("/{id}") 44 | @Consumes({MediaType.WILDCARD}) 45 | @Produces({MediaType.WILDCARD}) 46 | @DELETE 47 | public void delete(@PathParam("id") String id) throws ServiceException; 48 | 49 | } 50 | -------------------------------------------------------------------------------- /enterprise-granny-phonelib/src/main/webapp/WEB-INF/spring/spring-context.xml: -------------------------------------------------------------------------------- 1 | <?xml version="1.0" encoding="UTF-8"?> 2 | <beans xmlns="http://www.springframework.org/schema/beans" 3 | xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 4 | xmlns:jaxrs="http://cxf.apache.org/jaxrs" 5 | xmlns:context="http://www.springframework.org/schema/context" 6 | xmlns:beans="http://www.springframework.org/schema/beans" 7 | xsi:schemaLocation= 8 | "http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd 9 | http://cxf.apache.org/jaxrs http://cxf.apache.org/schemas/jaxrs.xsd 10 | http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd"> 11 | 12 | <!-- Root Context: defines shared resources visible to all other web components --> 13 | 14 | <!-- import CXF JAX-RS config --> 15 | <beans:import resource="classpath:META-INF/cxf/cxf.xml" /> 16 | <beans:import resource="classpath:META-INF/cxf/cxf-servlet.xml" /> 17 | 18 | <context:component-scan base-package="com.sap.hana.cloud.samples.granny.libphonenumber" /> 19 | 20 | <jaxrs:server id="api" address="/v1"> 21 | <jaxrs:properties> 22 | <entry key="org.apache.cxf.propagate.exception" value="false" /> 23 | </jaxrs:properties> 24 | <jaxrs:serviceBeans> 25 | <ref bean="libPhonenumberService" /> 26 | </jaxrs:serviceBeans> 27 | <jaxrs:providers> 28 | <ref bean="jacksonProvider" /> 29 | </jaxrs:providers> 30 | </jaxrs:server> 31 | 32 | <bean id="jacksonProvider" class="org.codehaus.jackson.jaxrs.JacksonJaxbJsonProvider"/> 33 | 34 | </beans> 35 | -------------------------------------------------------------------------------- /enterprise-granny-phonelib/src/main/resources/META-INF/enunciate/enunciate.xml: -------------------------------------------------------------------------------- 1 | <enunciate xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 2 | xsi:noNamespaceSchemaLocation="http://enunciate.webcohesion.com/schemas/enunciate-2.5.0.xsd"> 3 | 4 | 5 | <title>googlei18n/libphonenumber 6 | Provides a microservice for Google's libphonenumber library. 7 | Apache License, Version 2.0 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 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 | -------------------------------------------------------------------------------- /enterprise-granny-client/src/main/webapp/WEB-INF/web.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Addressbook-Service 5 | com.sap.core.connectivity.api.http.HttpDestination 6 | 7 | 8 | contextConfigLocation 9 | /WEB-INF/spring/root-context.xml 10 | 11 | 12 | org.springframework.web.context.ContextLoaderListener 13 | 14 | 15 | appServlet 16 | org.springframework.web.servlet.DispatcherServlet 17 | 18 | contextConfigLocation 19 | /WEB-INF/spring/appServlet/servlet-context.xml 20 | 21 | 1 22 | 23 | 24 | appServlet 25 | / 26 | 27 | 28 | JPAXFilter 29 | JPAXFilter 30 | com.sap.hana.cloud.samples.granny.client.web.JPAXFilter 31 | 32 | 33 | JPAXFilter 34 | /* 35 | 36 | -------------------------------------------------------------------------------- /CREDITS: -------------------------------------------------------------------------------- 1 | This program references/bundles the following third party open source or other free download components. 2 | The third party licensors of these components may provide additional license rights, 3 | terms and conditions and/or require certain notices as described below. 4 | 5 | jQuery (http://jquery.com/) 6 | Licensed under MIT - https://github.com/jquery/jquery/blob/master/MIT-LICENSE.txt 7 | 8 | jquery-pjax (https://github.com/defunkt/jquery-pjax) 9 | Licensed under MIT - https://github.com/defunkt/jquery-pjax/blob/master/LICENSE 10 | 11 | Twitter Bootstrap (http://twitter.github.com/bootstrap/) 12 | Licensed under Apache License, Version 2.0 - http://www.apache.org/licenses/LICENSE-2.0 13 | 14 | html5shiv (https://code.google.com/p/html5shiv/) 15 | Licensed under MIT - http://www.opensource.org/licenses/mit-license.php 16 | 17 | Respond.js (https://github.com/scottjehl/Respond) 18 | Licensed under MIT - http://www.opensource.org/licenses/mit-license.php 19 | 20 | GLYPHICONS Halflings (http://glyphicons.com/) 21 | Licensed as part of Bootstrap from Twitter, http://glyphicons.com/license/ 22 | 23 | ------- 24 | 25 | Elderly Woman designed by Peacock Dream from the Noun Project (http://thenounproject.com/term/elderly-woman/9727/) 26 | Licensed under Creative Commons – Attribution (CC BY 3.0) - http://creativecommons.org/licenses/by/3.0/us/ 27 | 28 | Bebas Neue Font by Ryoichi Tsunekawa (http://dharmatype.com/post/84312257192/bebas-neue) 29 | Licensed under SIL Open Font License - http://en.wikipedia.org/wiki/SIL_Open_Font_License 30 | 31 | Pacifico Font by Vernon Adams (http://code.newtypography.co.uk/pacifico-opentype-features/) 32 | Licensed under SIL Open Font License - http://www.fontsquirrel.com/license/pacifico 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /enterprise-granny-client/src/main/java/com/sap/hana/cloud/samples/granny/client/web/JPAXFilter.java: -------------------------------------------------------------------------------- 1 | package com.sap.hana.cloud.samples.granny.client.web; 2 | 3 | import java.io.IOException; 4 | 5 | import javax.servlet.Filter; 6 | import javax.servlet.FilterChain; 7 | import javax.servlet.FilterConfig; 8 | import javax.servlet.ServletException; 9 | import javax.servlet.ServletRequest; 10 | import javax.servlet.ServletResponse; 11 | import javax.servlet.http.HttpServletRequest; 12 | import javax.servlet.http.HttpServletResponse; 13 | 14 | /** 15 | * Servlet Filter implementation class JPAXFilter 16 | */ 17 | public class JPAXFilter implements Filter 18 | { 19 | 20 | /** 21 | * Default constructor. 22 | */ 23 | public JPAXFilter() 24 | { 25 | // TODO Auto-generated constructor stub 26 | } 27 | 28 | /** 29 | * @see Filter#destroy() 30 | */ 31 | public void destroy() 32 | { 33 | // TODO Auto-generated method stub 34 | } 35 | 36 | /** 37 | * @see Filter#doFilter(ServletRequest, ServletResponse, FilterChain) 38 | */ 39 | public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException 40 | { 41 | HttpServletRequest req = (HttpServletRequest) request; 42 | HttpServletResponse resp = (HttpServletResponse) response; 43 | 44 | // pass the request along the filter chain 45 | chain.doFilter(request, response); 46 | 47 | if (req.getHeader("x-pjax") != null) 48 | { 49 | //resp.getWriter().println("JPAX enabled!"); 50 | resp.setContentType("text/html"); 51 | } 52 | 53 | } 54 | 55 | /** 56 | * @see Filter#init(FilterConfig) 57 | */ 58 | public void init(FilterConfig fConfig) throws ServletException 59 | { 60 | // TODO Auto-generated method stub 61 | } 62 | 63 | } 64 | -------------------------------------------------------------------------------- /enterprise-granny-service/src/test/java/com/osintegrators/example/TestAddressService.java: -------------------------------------------------------------------------------- 1 | package com.osintegrators.example; 2 | 3 | import static org.junit.Assert.assertEquals; 4 | 5 | import org.junit.After; 6 | import org.junit.Before; 7 | import org.springframework.beans.factory.annotation.Autowired; 8 | 9 | /** 10 | * @author dmistry 11 | * @deprecated In favor of {@link com.sap.hana.cloud.samples.granny.dao.TestContactDAO} 12 | */ 13 | //@ContextConfiguration 14 | //@RunWith(SpringJUnit4ClassRunner.class) 15 | public class TestAddressService { 16 | 17 | @Autowired 18 | AddressService addressService; 19 | 20 | /** 21 | * @throws java.lang.Exception 22 | */ 23 | @Before 24 | public void setUp() throws Exception { 25 | } 26 | 27 | /** 28 | * @throws java.lang.Exception 29 | */ 30 | @After 31 | public void tearDown() throws Exception { 32 | } 33 | 34 | //@Test 35 | public void testCreateAddress() { 36 | String expectedName = "John Doe"; 37 | String expectedAddress = "345 West Main St\nDurham, NC"; 38 | String expectedPhone = "+1.919.321.0119"; 39 | String expectedEmail = "spam@osintegrators.com"; 40 | Address address = createAddressObject(expectedName, expectedAddress, 41 | expectedPhone, expectedEmail); 42 | addressService.createAddress(address); 43 | Address result = addressService.getAddressById(1L); 44 | 45 | assertEquals(expectedName, address.getName()); 46 | assertEquals(expectedAddress, address.getAddress()); 47 | assertEquals(expectedPhone, address.getPhone()); 48 | assertEquals(expectedEmail, address.getEmail()); 49 | } 50 | 51 | private Address createAddressObject(String name, String address, String phone, String email) { 52 | Address address1 = new Address(name, address, phone, email); 53 | return address1; 54 | } 55 | } -------------------------------------------------------------------------------- /enterprise-granny-client/src/main/webapp/WEB-INF/tags/input.tag: -------------------------------------------------------------------------------- 1 | <%@tag description="Extended input tag to allow for sophisticated errors" pageEncoding="UTF-8"%> 2 | <%@taglib prefix="spring" uri="http://www.springframework.org/tags" %> 3 | <%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> 4 | <%@taglib prefix="form" uri="http://www.springframework.org/tags/form" %> 5 | <%@taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %> 6 | 7 | <%@attribute name="path" required="true" type="java.lang.String"%> 8 | <%@attribute name="labelClass" required="false" type="java.lang.String"%> 9 | <%@attribute name="controlClass" required="false" type="java.lang.String"%> 10 | <%@attribute name="label" required="false" type="java.lang.String"%> 11 | <%@attribute name="required" required="false" type="java.lang.Boolean"%> 12 | <%@attribute name="placeholder" required="false" type="java.lang.String"%> 13 | <%@attribute name="type" required="false" type="java.lang.String"%> 14 | 15 | 16 | 17 | 18 | 19 | 20 |
21 | 22 |
23 | 24 | 25 | ${status.errorMessage} 26 | 27 |
28 |
29 |
-------------------------------------------------------------------------------- /enterprise-granny-service/src/main/java/com/sap/hana/cloud/samples/granny/web/util/ParserExceptionMapper.java: -------------------------------------------------------------------------------- 1 | package com.sap.hana.cloud.samples.granny.web.util; 2 | 3 | import javax.inject.Inject; 4 | import javax.ws.rs.core.MediaType; 5 | import javax.ws.rs.core.Response; 6 | import javax.ws.rs.ext.ExceptionMapper; 7 | 8 | import org.codehaus.jackson.JsonParseException; 9 | import org.codehaus.jackson.map.ObjectMapper; 10 | 11 | import com.sap.hana.cloud.samples.granny.model.StatusMessage; 12 | import com.sap.hana.cloud.samples.granny.util.CustomObjectMapper; 13 | 14 | /** 15 | * 16 | * @see http://cxf.apache.org/docs/jax-rs-basics.html#JAX-RSBasics-Exceptionhandling 17 | * @see http://fusesource.com/docs/esb/4.2/rest/RESTExceptionMapper.html 18 | * 19 | */ 20 | public class ParserExceptionMapper implements ExceptionMapper 21 | { 22 | 23 | /** 24 | * The {@link ObjectMapper} to be used. 25 | * 26 | * @see CustomObjectMapper 27 | */ 28 | @Inject 29 | ObjectMapper objectMapper = null; 30 | 31 | 32 | /** 33 | * TODO 34 | * 35 | * @param exception The caught {@link JsonParseException} 36 | * @return The corresponding {@link Response} containing a {@link StatusMessage} 37 | */ 38 | @Override 39 | public Response toResponse(JsonParseException exception) 40 | { 41 | final String PATTERN = "\\n at \\[Source: org.apache.cxf.transport.http.AbstractHTTPDestination\\$.{1,12};\\s"; 42 | final String REPLACE = " at location: ["; 43 | 44 | StatusMessage message = new StatusMessage(); 45 | 46 | message.setCode(400); 47 | message.setDescription(exception.getMessage().replaceAll(PATTERN, REPLACE)); 48 | message.setError("Parsing error"); 49 | 50 | return Response.status(message.getCode()).entity(message).type(MediaType.APPLICATION_JSON).build(); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /enterprise-granny-service/src/main/java/com/sap/hana/cloud/samples/granny/xcc/validations/ValidPhoneNumberValidator.java: -------------------------------------------------------------------------------- 1 | package com.sap.hana.cloud.samples.granny.xcc.validations; 2 | 3 | import java.util.Locale; 4 | 5 | import javax.validation.ConstraintValidator; 6 | import javax.validation.ConstraintValidatorContext; 7 | 8 | import org.springframework.context.MessageSource; 9 | import org.springframework.context.MessageSourceAware; 10 | 11 | import com.google.i18n.phonenumbers.NumberParseException; 12 | import com.google.i18n.phonenumbers.PhoneNumberUtil; 13 | import com.sap.hana.cloud.samples.granny.model.PhoneNumber; 14 | 15 | 16 | public class ValidPhoneNumberValidator implements ConstraintValidator, MessageSourceAware 17 | { 18 | MessageSource messageSource; 19 | 20 | /** 21 | * The {@link PhoneNumberUtil} instance used to validate {@link PhoneNumber}s. 22 | */ 23 | private final static PhoneNumberUtil phoneUtil = PhoneNumberUtil.getInstance(); 24 | 25 | @Override 26 | public void initialize(ValidPhoneNumber constraintAnnotation) 27 | { 28 | } 29 | 30 | @Override 31 | public boolean isValid(String value, ConstraintValidatorContext context) 32 | { 33 | 34 | com.google.i18n.phonenumbers.Phonenumber.PhoneNumber no = null; 35 | 36 | try 37 | { 38 | no = phoneUtil.parse(value, Locale.getDefault().getCountry()); 39 | } 40 | catch (NumberParseException e) 41 | { 42 | // TODO Auto-generated catch block 43 | e.printStackTrace(); 44 | } 45 | 46 | boolean valid = phoneUtil.isValidNumber(no); 47 | 48 | 49 | return valid; 50 | } 51 | 52 | @Override 53 | public void setMessageSource(MessageSource messageSource) 54 | { 55 | this.messageSource = messageSource; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /enterprise-granny-service/src/main/resources/META-INF/enunciate/enunciate.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | Granny's Addressbook - Contact Management Service 6 | Provides REST endpoints to manage address data. 7 | SAP SE 8 | 9 | Apache License, Version 2.0 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 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 | -------------------------------------------------------------------------------- /enterprise-granny-service/src/main/resources/log4j.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 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 | -------------------------------------------------------------------------------- /enterprise-granny-service/src/main/webapp/WEB-INF/web.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | jdbc/DefaultDB 8 | javax.sql.DataSource 9 | 10 | 11 | PhoneNumber-Service 12 | com.sap.core.connectivity.api.http.HttpDestination 13 | 14 | 15 | contextConfigLocation 16 | /WEB-INF/spring/spring-context.xml 17 | 18 | 19 | contextInitializerClasses 20 | com.sap.hana.cloud.samples.granny.web.util.EnvironmentContextInitializer 21 | 22 | 23 | 24 | org.springframework.web.context.ContextLoaderListener 25 | 26 | 27 | CXFServlet 28 | org.apache.cxf.transport.servlet.CXFServlet 29 | 1 30 | 31 | 32 | CXFServlet 33 | /api/* 34 | 35 | 36 | RequestContextFilter 37 | org.springframework.web.filter.RequestContextFilter 38 | 39 | 40 | RequestContextFilter 41 | /api/* 42 | 43 | 44 | index.html 45 | 46 | 47 | -------------------------------------------------------------------------------- /enterprise-granny-client/src/main/webapp/WEB-INF/spring/root-context.xml: -------------------------------------------------------------------------------- 1 | 2 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | i18n/messages 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | ${messageResourceBundleName} 35 | 36 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /enterprise-granny-service/src/main/java/com/sap/hana/cloud/samples/granny/web/util/CustomJAXRSBeanValidationInInterceptor.java: -------------------------------------------------------------------------------- 1 | package com.sap.hana.cloud.samples.granny.web.util; 2 | 3 | import java.lang.reflect.Method; 4 | import java.util.List; 5 | 6 | import org.apache.cxf.jaxrs.validation.JAXRSBeanValidationInInterceptor; 7 | import org.apache.cxf.message.Message; 8 | import org.apache.cxf.validation.BeanValidationProvider; 9 | 10 | /** 11 | * Custom implementation of {@link JAXRSBeanValidationInInterceptor} to overwrite the standard behavior of 12 | * calling {@link BeanValidationProvider#validateParameters(Object, Method, Object[])} in favor of calling 13 | * {@link BeanValidationProvider#validateBean(Object)}. 14 | * 15 | * The issue with the standard implementation is that it includes the method name as part of the 16 | * {@link javax.validation.ConstraintViolation#getPropertyPath()}. 17 | * 18 | * @see BeanValidationInInterceptor#handleValidation(Message, Object, Method, List) 19 | * 20 | */ 21 | public class CustomJAXRSBeanValidationInInterceptor extends JAXRSBeanValidationInInterceptor 22 | { 23 | /* 24 | * (non-Javadoc) 25 | * @see org.apache.cxf.validation.BeanValidationInInterceptor#handleValidation(org.apache.cxf.message.Message, java.lang.Object, java.lang.reflect.Method, java.util.List) 26 | */ 27 | @Override 28 | protected void handleValidation(final Message message, final Object resourceInstance, 29 | final Method method, final List arguments) 30 | { 31 | if (arguments.size() > 0) 32 | { 33 | BeanValidationProvider provider = getProvider(message); 34 | 35 | for (Object obj : arguments) 36 | { 37 | if (obj != null) 38 | { 39 | provider.validateBean(obj); 40 | } 41 | } 42 | 43 | message.getExchange().put(BeanValidationProvider.class, provider); 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013, 2014 SAP SE 2 | 3 | Except as provided below, this software is licensed under the Apache License, Version 2.0 (the "License"); you may not use this software except in compliance with the License.You may obtain a copy of the License at: 4 | 5 | http://www.apache.org/licenses/LICENSE-2.0 6 | 7 | Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. 8 | ---------------------------------------------------------------------------------------------- 9 | This software includes the following component that is subject to the following terms: 10 | 11 | JavaSpringGranny - Granny's Addressbook with Java + Spring MVC + PostgreSQL 12 | 13 | Avaiable at https://github.com/osintegrators/JavaSpringGranny 14 | 15 | Copyright (c) 2012 Open Software Integrators 16 | 17 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 18 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 19 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 20 | 21 | -------------------------------------------------------------------------------- /enterprise-granny-service/src/main/java/com/osintegrators/example/Address.java: -------------------------------------------------------------------------------- 1 | package com.osintegrators.example; 2 | 3 | import javax.persistence.Column; 4 | import javax.persistence.Entity; 5 | import javax.persistence.GeneratedValue; 6 | import javax.persistence.GenerationType; 7 | import javax.persistence.Id; 8 | import javax.persistence.NamedQueries; 9 | import javax.persistence.NamedQuery; 10 | 11 | /** 12 | * @deprecated please use {@link com.sap.hana.cloud.samples.granny.model.Contact} instead 13 | */ 14 | @Entity 15 | @NamedQueries({ 16 | @NamedQuery(name = "Address.findAll", query = "select a from Address a") }) 17 | public class Address { 18 | 19 | @Id 20 | @Column(name="ID") 21 | @GeneratedValue(strategy = GenerationType.AUTO) 22 | private String _id; // TODO revert back to Long once the srv/ui layer are updated to use new model! 23 | 24 | private String name; 25 | private String address; 26 | private String phone; 27 | private String email; 28 | 29 | public Address(String name, String address, String phoneNumber, String email) { 30 | this.name = name; 31 | this.address = address; 32 | this.phone = phoneNumber; 33 | this.email = email; 34 | } 35 | 36 | // ADDED Default Constructor Because of Build Errors in pom.xml 37 | public Address() { 38 | } 39 | 40 | public String get_id() { 41 | return _id; 42 | } 43 | 44 | public void set_id(String _id) { 45 | this._id = _id; 46 | } 47 | 48 | public String getName() { 49 | return name; 50 | } 51 | 52 | public void setName(String name) { 53 | this.name = name; 54 | } 55 | 56 | public String getAddress() { 57 | return address; 58 | } 59 | 60 | public void setAddress(String address) { 61 | this.address = address; 62 | } 63 | 64 | public String getPhone() { 65 | return phone; 66 | } 67 | 68 | public void setPhone(String phoneNumber) { 69 | this.phone = phoneNumber; 70 | } 71 | 72 | public String getEmail() { 73 | return email; 74 | } 75 | 76 | public void setEmail(String email) { 77 | this.email = email; 78 | } 79 | 80 | } 81 | -------------------------------------------------------------------------------- /enterprise-granny-service/src/main/java/com/sap/hana/cloud/samples/granny/web/util/JsonMappingExceptionMapper.java: -------------------------------------------------------------------------------- 1 | package com.sap.hana.cloud.samples.granny.web.util; 2 | 3 | import javax.inject.Inject; 4 | import javax.ws.rs.core.MediaType; 5 | import javax.ws.rs.core.Response; 6 | import javax.ws.rs.ext.ExceptionMapper; 7 | 8 | import org.codehaus.jackson.map.JsonMappingException; 9 | import org.codehaus.jackson.map.ObjectMapper; 10 | 11 | import com.sap.hana.cloud.samples.granny.model.StatusMessage; 12 | import com.sap.hana.cloud.samples.granny.util.CustomObjectMapper; 13 | 14 | /** 15 | * 16 | * @see http://cxf.apache.org/docs/jax-rs-basics.html#JAX-RSBasics-Exceptionhandling 17 | * @see http://fusesource.com/docs/esb/4.2/rest/RESTExceptionMapper.html 18 | * 19 | */ 20 | public class JsonMappingExceptionMapper implements ExceptionMapper 21 | { 22 | 23 | /** 24 | * The {@link ObjectMapper} to be used. 25 | * 26 | * @see CustomObjectMapper 27 | */ 28 | @Inject 29 | ObjectMapper objectMapper = null; 30 | 31 | 32 | /** 33 | * TODO 34 | * 35 | * @param exception The caught {@link JsonMappingException} 36 | * @return The corresponding {@link Response} containing a {@link StatusMessage} 37 | */ 38 | @Override 39 | public Response toResponse(JsonMappingException exception) 40 | { 41 | final String PACKAGE_NAME = "com.sap.hana.cloud.samples.granny.model."; 42 | 43 | final String PATTERN = "\\n at \\[Source: org.apache.cxf.transport.http.AbstractHTTPDestination\\$.{1,12};\\s"; 44 | final String REPLACE = " at location: ["; 45 | 46 | String msgStr = exception.getMessage().replaceAll(PACKAGE_NAME, ""); // remove full-qualified package names 47 | msgStr = msgStr.replaceAll(PATTERN,REPLACE); 48 | 49 | StatusMessage message = new StatusMessage(); 50 | message.setCode(400); 51 | message.setDescription(msgStr); 52 | message.setError("Mapping error"); 53 | 54 | return Response.status(message.getCode()).entity(message).type(MediaType.APPLICATION_JSON).build(); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /enterprise-granny-service/src/main/java/com/sap/hana/cloud/samples/granny/web/util/EnvironmentContextInitializer.java: -------------------------------------------------------------------------------- 1 | package com.sap.hana.cloud.samples.granny.web.util; 2 | 3 | import org.slf4j.Logger; 4 | import org.slf4j.LoggerFactory; 5 | import org.springframework.context.ApplicationContextInitializer; 6 | import org.springframework.context.ConfigurableApplicationContext; 7 | 8 | /** 9 | * Detects the current landscape the application is run on and initializes the Spring {@link ApplicationContext} accordingly. 10 | * 11 | * @see ApplicationContextInitializer 12 | */ 13 | public class EnvironmentContextInitializer implements ApplicationContextInitializer 14 | { 15 | private static final Logger logger = LoggerFactory.getLogger(EnvironmentContextInitializer.class); 16 | 17 | public static enum RuntimeEnvironment { LOCAL, CLOUD }; 18 | 19 | /* 20 | * (non-Javadoc) 21 | * @see org.springframework.context.ApplicationContextInitializer#initialize(org.springframework.context.ConfigurableApplicationContext) 22 | */ 23 | @Override 24 | public void initialize(ConfigurableApplicationContext applicationContext) 25 | { 26 | RuntimeEnvironment stage = getEnvironment(); 27 | 28 | applicationContext.getEnvironment().setActiveProfiles(stage.name().toLowerCase()); 29 | } 30 | 31 | /** 32 | * Returns the {@link RuntimeEnvironment} the application runs in. Defaults to CLOUD. 33 | * 34 | * @return The {@link RuntimeEnvironment} the application runs in 35 | */ 36 | public static RuntimeEnvironment getEnvironment() 37 | { 38 | RuntimeEnvironment retVal = RuntimeEnvironment.CLOUD; 39 | 40 | final String landscape = System.getenv("HC_LANDSCAPE"); // NEO 41 | final String vcap = System.getenv("VCAP_APPLICATION"); // Cloud Foundry 42 | 43 | if (landscape == null && vcap == null) 44 | { 45 | retVal = RuntimeEnvironment.LOCAL; 46 | } 47 | 48 | if (logger.isInfoEnabled()) 49 | { 50 | logger.info("Application running in '{}' environment", retVal.name().toLowerCase()); 51 | } 52 | 53 | return retVal; 54 | } 55 | } -------------------------------------------------------------------------------- /enterprise-granny-service/src/main/java/com/sap/hana/cloud/samples/granny/web/util/CustomJAXRSParameterNameProvider.java: -------------------------------------------------------------------------------- 1 | package com.sap.hana.cloud.samples.granny.web.util; 2 | 3 | import java.lang.reflect.Constructor; 4 | import java.lang.reflect.Method; 5 | import java.util.ArrayList; 6 | import java.util.List; 7 | 8 | import javax.validation.ParameterNameProvider; 9 | 10 | import org.apache.cxf.jaxrs.model.Parameter; 11 | import org.apache.cxf.jaxrs.utils.ResourceUtils; 12 | import org.apache.cxf.jaxrs.validation.JAXRSParameterNameProvider; 13 | 14 | /** 15 | * Custom implementation of the {@link ParameterNameProvider} to provide more control about the parameter names determined during the validation of data. 16 | */ 17 | public class CustomJAXRSParameterNameProvider extends JAXRSParameterNameProvider implements ParameterNameProvider 18 | { 19 | @Override 20 | public List getParameterNames(final Constructor< ? > constructor) 21 | { 22 | final List< String > parameterNames = new ArrayList< String >(); 23 | 24 | for (int i = 0; i < constructor.getParameterTypes().length; ++i) 25 | { 26 | parameterNames.add(""); 27 | } 28 | 29 | return parameterNames; 30 | } 31 | 32 | /** 33 | * 34 | * @see org.apache.cxf.jaxrs.validation.JAXRSParameterNameProvider#getParameterNames(java.lang.reflect.Method) 35 | */ 36 | @Override 37 | public List getParameterNames(final Method method) 38 | { 39 | final List parameters = ResourceUtils.getParameters(method); 40 | final List parameterNames = new ArrayList(); 41 | 42 | for (int i = 0; i < parameters.size(); ++i) 43 | { 44 | final StringBuilder sb = new StringBuilder(); 45 | 46 | Parameter parameter = parameters.get(i); 47 | 48 | if (parameter.getName() != null) 49 | { 50 | sb.append(parameter.getType().toString()); 51 | sb.append("(\"" + parameter.getName() + "\")"); 52 | sb.append(" "); 53 | } 54 | sb.append(method.getParameterTypes()[i].getSimpleName().toLowerCase()); 55 | 56 | parameterNames.add(""); 57 | } 58 | 59 | return parameterNames; 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /enterprise-granny-core/src/main/java/com/sap/hana/cloud/samples/granny/srv/ContactService.java: -------------------------------------------------------------------------------- 1 | package com.sap.hana.cloud.samples.granny.srv; 2 | 3 | import java.util.List; 4 | 5 | import com.sap.hana.cloud.samples.granny.model.Contact; 6 | 7 | /** 8 | * Provides CRUD (Create, Read, Update, Delete) operations as well as other 9 | * life-cycle related functions for {@link Contact} objects. 10 | */ 11 | public interface ContactService 12 | { 13 | 14 | /** 15 | * Creates the specified {@link Contact}. 16 | * 17 | * @param contact The {@link Contact} to create 18 | * @return The {@link Contact} 19 | * @throws ServiceException In case of an error 20 | */ 21 | Contact createContact(Contact contact) throws ServiceException; 22 | 23 | /** 24 | * Saves the specified {@link Contact}. 25 | * 26 | * @param contact The {@link Contact} to save 27 | * @return The {@link Contact} 28 | * @throws ServiceException In case of an error 29 | */ 30 | Contact saveContact(Contact contact) throws ServiceException; 31 | 32 | /** 33 | * Deletes the specified {@link Contact}. 34 | * 35 | * @param contact The {@link Contact} to delete 36 | * @throws ServiceException In case of an error 37 | */ 38 | void deleteContact(Contact contact) throws ServiceException; 39 | 40 | /** 41 | * Returns a {@link List} of all {@link Contact} objects. 42 | * 43 | * @return {@link List} of all {@link Contact} objects 44 | * @throws ServiceException In case of an error 45 | */ 46 | List getAllContactes() throws ServiceException; 47 | 48 | /** 49 | * Returns the {@link Contact} with the specified ID or NULL if no {@link Contact} object with the specified ID exists. 50 | * 51 | * @param id The id of the {@link Contact} to retrieve 52 | * @return The {@link Contact} with the specified ID or NULL if no {@link Contact} object with the specified ID exists 53 | * @throws ServiceException In case of an error 54 | */ 55 | Contact getContactById(String id) throws ServiceException; 56 | 57 | /** 58 | * Updates the specified {@link Contact}. 59 | * 60 | * @param contact The {@link Contact} to update 61 | * @return The {@link Contact} 62 | * @throws ServiceException In case of an error 63 | */ 64 | Contact updateContact(Contact contact) throws ServiceException; 65 | 66 | } 67 | -------------------------------------------------------------------------------- /enterprise-granny-service/src/main/java/com/sap/hana/cloud/samples/granny/dao/ContactDAOImpl.java: -------------------------------------------------------------------------------- 1 | package com.sap.hana.cloud.samples.granny.dao; 2 | 3 | import java.util.List; 4 | 5 | import javax.validation.Valid; 6 | 7 | import org.springframework.beans.factory.annotation.Autowired; 8 | import org.springframework.stereotype.Service; 9 | 10 | import com.sap.hana.cloud.samples.granny.model.Contact; 11 | 12 | /** 13 | * Default implementation of the {@link ContactDAO} interface based on Spring Data JPA. 14 | * 15 | * @see http://static.springsource.org/spring-data/data-jpa/docs/current/reference/html/ 16 | */ 17 | @Service 18 | public class ContactDAOImpl implements ContactDAO 19 | { 20 | 21 | /** 22 | * The spring-data-jpa repository to be used for persistence operations. 23 | */ 24 | @Autowired 25 | ContactRepository repository = null; 26 | 27 | @Override 28 | public List findAll() 29 | { 30 | return repository.queryAll(); 31 | } 32 | 33 | @Override 34 | public Contact save(@Valid Contact entity) 35 | { 36 | return repository.save(entity); 37 | } 38 | 39 | @SuppressWarnings("unchecked") 40 | @Override 41 | public Iterable save(@Valid Iterable entities) 42 | { 43 | return (Iterable) repository.save(entities); 44 | } 45 | 46 | @Override 47 | public Contact findOne(String id) 48 | { 49 | return repository.findOne(id); 50 | } 51 | 52 | @Override 53 | public boolean exists(String id) 54 | { 55 | return repository.exists(id); 56 | } 57 | 58 | @Override 59 | public long count() 60 | { 61 | return repository.count(); 62 | } 63 | 64 | @Override 65 | public void delete(String id) 66 | { 67 | repository.delete(id); 68 | } 69 | 70 | @Override 71 | public void delete(Contact entity) 72 | { 73 | repository.delete(entity); 74 | } 75 | 76 | @Override 77 | public void delete(Iterable entities) 78 | { 79 | repository.delete(entities); 80 | } 81 | 82 | @Override 83 | public void deleteAll() 84 | { 85 | repository.deleteAll(); 86 | } 87 | 88 | @Override 89 | public List findByAddressesCountry(String country) 90 | { 91 | return repository.findByAddressesCountry(country); 92 | } 93 | 94 | } 95 | -------------------------------------------------------------------------------- /diagrams/enterprise-granny.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | com.sap.hana.cloud.samples.granny.model.PhoneNumber 4 | 120 5 | 90 6 | 450 7 | 378 8 | false 9 | false 10 | false 11 | 12 | 13 | 14 | com.sap.hana.cloud.samples.granny.model.BaseObject 15 | 120 16 | 188 17 | 210 18 | 50 19 | false 20 | false 21 | false 22 | 23 | 24 | 25 | com.sap.hana.cloud.samples.granny.model.Contact 26 | 120 27 | 208 28 | 210 29 | 260 30 | false 31 | false 32 | false 33 | 34 | 35 | 36 | com.sap.hana.cloud.samples.granny.model.Address 37 | 120 38 | 190 39 | 20 40 | 210 41 | false 42 | false 43 | false 44 | 45 | 46 | 47 | com.sap.hana.cloud.samples.granny.model.EmailAddress 48 | 120 49 | 90 50 | 442 51 | 190 52 | false 53 | false 54 | false 55 | 56 | -------------------------------------------------------------------------------- /enterprise-granny-client/src/main/webapp/resources/js/html5shiv.js: -------------------------------------------------------------------------------- 1 | /* 2 | HTML5 Shiv v3.7.0 | @afarkas @jdalton @jon_neal @rem | MIT/GPL2 Licensed 3 | */ 4 | (function(l,f){function m(){var a=e.elements;return"string"==typeof a?a.split(" "):a}function i(a){var b=n[a[o]];b||(b={},h++,a[o]=h,n[h]=b);return b}function p(a,b,c){b||(b=f);if(g)return b.createElement(a);c||(c=i(b));b=c.cache[a]?c.cache[a].cloneNode():r.test(a)?(c.cache[a]=c.createElem(a)).cloneNode():c.createElem(a);return b.canHaveChildren&&!s.test(a)?c.frag.appendChild(b):b}function t(a,b){if(!b.cache)b.cache={},b.createElem=a.createElement,b.createFrag=a.createDocumentFragment,b.frag=b.createFrag(); 5 | a.createElement=function(c){return!e.shivMethods?b.createElem(c):p(c,a,b)};a.createDocumentFragment=Function("h,f","return function(){var n=f.cloneNode(),c=n.createElement;h.shivMethods&&("+m().join().replace(/[\w\-]+/g,function(a){b.createElem(a);b.frag.createElement(a);return'c("'+a+'")'})+");return n}")(e,b.frag)}function q(a){a||(a=f);var b=i(a);if(e.shivCSS&&!j&&!b.hasCSS){var c,d=a;c=d.createElement("p");d=d.getElementsByTagName("head")[0]||d.documentElement;c.innerHTML="x"; 6 | c=d.insertBefore(c.lastChild,d.firstChild);b.hasCSS=!!c}g||t(a,b);return a}var k=l.html5||{},s=/^<|^(?:button|map|select|textarea|object|iframe|option|optgroup)$/i,r=/^(?:a|b|code|div|fieldset|h1|h2|h3|h4|h5|h6|i|label|li|ol|p|q|span|strong|style|table|tbody|td|th|tr|ul)$/i,j,o="_html5shiv",h=0,n={},g;(function(){try{var a=f.createElement("a");a.innerHTML="";j="hidden"in a;var b;if(!(b=1==a.childNodes.length)){f.createElement("a");var c=f.createDocumentFragment();b="undefined"==typeof c.cloneNode|| 7 | "undefined"==typeof c.createDocumentFragment||"undefined"==typeof c.createElement}g=b}catch(d){g=j=!0}})();var e={elements:k.elements||"abbr article aside audio bdi canvas data datalist details dialog figcaption figure footer header hgroup main mark meter nav output progress section summary template time video",version:"3.7.0",shivCSS:!1!==k.shivCSS,supportsUnknownElements:g,shivMethods:!1!==k.shivMethods,type:"default",shivDocument:q,createElement:p,createDocumentFragment:function(a,b){a||(a=f); 8 | if(g)return a.createDocumentFragment();for(var b=b||i(a),c=b.frag.cloneNode(),d=0,e=m(),h=e.length;d> countryMap = new HashMap>(); 16 | 17 | 18 | /** 19 | * Returns a {@link SortedMap} containing the ISO 3166 code of all countries as the key and the localized name 20 | * of the country as the value. 21 | * 22 | * @param locale The {@link Locale} used to obtain the localized names of the countries 23 | * @return {@link SortedMap} containing the ISO 3166 code of all countries as the key and the localized name 24 | * of the country as the value. 25 | */ 26 | public static SortedMap getCountryList(Locale locale) 27 | { 28 | if (locale == null || locale.getLanguage() == null) 29 | { 30 | return new TreeMap(); // return empty map 31 | } 32 | 33 | SortedMap retVal = null; 34 | 35 | if (! countryMap.containsKey(locale.getLanguage())) 36 | { 37 | Map map = new HashMap(Locale.getISOCountries().length); 38 | 39 | for (String str : Locale.getISOCountries()) 40 | { 41 | map.put(str, new Locale("", str).getDisplayCountry(locale)); 42 | } 43 | 44 | retVal = sortByValues(map); 45 | countryMap.put(locale.getLanguage(), retVal); 46 | } 47 | 48 | retVal = countryMap.get(locale.getLanguage()); 49 | 50 | return retVal; 51 | 52 | } 53 | 54 | /** 55 | * Sorts the specified {@link Map} according to the natural ordering of its values. 56 | * 57 | * @param map The {@link Map} to sort 58 | * @return The {@link SortedMap} 59 | */ 60 | public static > SortedMap sortByValues(final Map map) 61 | { 62 | Comparator valueComparator = new Comparator() 63 | { 64 | public int compare(K k1, K k2) 65 | { 66 | int compare = map.get(k1).compareTo(map.get(k2)); 67 | if (compare == 0) return 1; 68 | else return compare; 69 | } 70 | }; 71 | 72 | SortedMap sortedByValues = new TreeMap(valueComparator); 73 | sortedByValues.putAll(map); 74 | return sortedByValues; 75 | } 76 | 77 | } 78 | -------------------------------------------------------------------------------- /enterprise-granny-client/src/main/java/com/sap/hana/cloud/samples/grannyv/client/web/util/LocaleUtils.java: -------------------------------------------------------------------------------- 1 | package com.sap.hana.cloud.samples.grannyv.client.web.util; 2 | 3 | import java.util.Comparator; 4 | import java.util.HashMap; 5 | import java.util.Locale; 6 | import java.util.Map; 7 | import java.util.SortedMap; 8 | import java.util.TreeMap; 9 | 10 | public class LocaleUtils 11 | { 12 | /** 13 | * Map containing the country selection lists as values and the language as key. 14 | */ 15 | private static Map> countryMap = new HashMap>(); 16 | 17 | 18 | /** 19 | * Returns a {@link SortedMap} containing the ISO 3166 code of all countries as the key and the localized name 20 | * of the country as the value. 21 | * 22 | * @param locale The {@link Locale} used to obtain the localized names of the countries 23 | * @return {@link SortedMap} containing the ISO 3166 code of all countries as the key and the localized name 24 | * of the country as the value. 25 | */ 26 | public static SortedMap getCountryList(Locale locale) 27 | { 28 | if (locale == null || locale.getLanguage() == null) 29 | { 30 | return new TreeMap(); // return empty map 31 | } 32 | 33 | SortedMap retVal = null; 34 | 35 | if (! countryMap.containsKey(locale.getLanguage())) 36 | { 37 | Map map = new HashMap(Locale.getISOCountries().length); 38 | 39 | for (String str : Locale.getISOCountries()) 40 | { 41 | map.put(str, new Locale("", str).getDisplayCountry(locale)); 42 | } 43 | 44 | retVal = sortByValues(map); 45 | countryMap.put(locale.getLanguage(), retVal); 46 | } 47 | 48 | retVal = countryMap.get(locale.getLanguage()); 49 | 50 | return retVal; 51 | 52 | } 53 | 54 | /** 55 | * Sorts the specified {@link Map} according to the natural ordering of its values. 56 | * 57 | * @param map The {@link Map} to sort 58 | * @return The {@link SortedMap} 59 | */ 60 | public static > SortedMap sortByValues(final Map map) 61 | { 62 | Comparator valueComparator = new Comparator() 63 | { 64 | public int compare(K k1, K k2) 65 | { 66 | int compare = map.get(k1).compareTo(map.get(k2)); 67 | if (compare == 0) return 1; 68 | else return compare; 69 | } 70 | }; 71 | 72 | SortedMap sortedByValues = new TreeMap(valueComparator); 73 | sortedByValues.putAll(map); 74 | return sortedByValues; 75 | } 76 | 77 | } 78 | -------------------------------------------------------------------------------- /enterprise-granny-service/src/test/java/com/sap/hana/cloud/samples/granny/dao/ValidPhoneNumberValidatorTest.java: -------------------------------------------------------------------------------- 1 | package com.sap.hana.cloud.samples.granny.dao; 2 | 3 | import static org.junit.Assert.assertTrue; 4 | 5 | import java.util.Locale; 6 | import java.util.Set; 7 | 8 | import javax.validation.ConstraintViolation; 9 | import javax.validation.Validator; 10 | import javax.validation.ValidatorFactory; 11 | 12 | import org.junit.Before; 13 | import org.junit.Test; 14 | import org.junit.runner.RunWith; 15 | import org.springframework.beans.factory.annotation.Autowired; 16 | import org.springframework.context.MessageSource; 17 | import org.springframework.test.context.ActiveProfiles; 18 | import org.springframework.test.context.ContextConfiguration; 19 | import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; 20 | 21 | import com.google.i18n.phonenumbers.NumberParseException; 22 | import com.google.i18n.phonenumbers.PhoneNumberUtil; 23 | import com.sap.hana.cloud.samples.granny.model.PhoneNumber; 24 | import com.sap.hana.cloud.samples.granny.xcc.validations.ValidPhoneNumberValidator; 25 | 26 | /** 27 | * Tests for the {@link ValidPhoneNumberValidator} class. 28 | */ 29 | @ContextConfiguration("classpath:/META-INF/spring/spring-persistence-config.xml") 30 | @RunWith(SpringJUnit4ClassRunner.class) 31 | @ActiveProfiles(profiles = "test") 32 | public class ValidPhoneNumberValidatorTest 33 | { 34 | 35 | @Autowired 36 | ValidatorFactory validator = null; 37 | 38 | @Autowired 39 | MessageSource source = null; 40 | 41 | private final static PhoneNumberUtil phoneUtil = PhoneNumberUtil.getInstance(); 42 | 43 | @Before 44 | public void setUp() throws Exception 45 | { 46 | } 47 | 48 | @Test 49 | public void testPhoneNumberUtil() 50 | { 51 | com.google.i18n.phonenumbers.Phonenumber.PhoneNumber no = null; 52 | 53 | try 54 | { 55 | no = phoneUtil.parse("+49 6227 712345", Locale.getDefault().getCountry()); 56 | } 57 | catch (NumberParseException e) 58 | { 59 | // TODO Auto-generated catch block 60 | e.printStackTrace(); 61 | } 62 | 63 | boolean valid = phoneUtil.isValidNumber(no); 64 | 65 | assertTrue("Number should be valid!", valid); 66 | } 67 | 68 | @Test 69 | public void testValidation() 70 | { 71 | PhoneNumber phoneNumber = new PhoneNumber(); 72 | phoneNumber.setNumber("+49 6227 712345"); 73 | 74 | Validator validator = this.validator.getValidator(); 75 | 76 | Set> constraintViolations = validator.validate(phoneNumber); 77 | 78 | assertTrue("Number should be valid!", constraintViolations.isEmpty()); 79 | 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /enterprise-granny-service/src/main/webapp/css/style.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Base structure 3 | */ 4 | 5 | /* Move down content because we have a fixed navbar that is 50px tall */ 6 | body { 7 | position:relative; 8 | padding-top: 50px; 9 | } 10 | 11 | .footer { 12 | width: 100%; 13 | /* Set the fixed height of the footer here */ 14 | height: 60px; 15 | background-color: #f5f5f5; 16 | } 17 | 18 | /* 19 | * Global add-ons 20 | */ 21 | .sub-header { 22 | padding-bottom: 10px; 23 | border-bottom: 1px solid #eee; 24 | } 25 | 26 | /* 27 | * Sidebar 28 | */ 29 | 30 | /* Hide for mobile, show later */ 31 | .sidebar { 32 | display: none; 33 | } 34 | @media (min-width: 768px) { 35 | .sidebar { 36 | position: fixed; 37 | top: 51px; 38 | bottom: 0; 39 | left: 0; 40 | z-index: 1000; 41 | display: block; 42 | padding: 20px; 43 | overflow-x: hidden; 44 | overflow-y: auto; /* Scrollable contents if viewport is shorter than content. */ 45 | background-color: #f5f5f5; 46 | border-right: 1px solid #eee; 47 | } 48 | } 49 | 50 | /* Sidebar navigation */ 51 | .nav-sidebar { 52 | margin-right: -21px; /* 20px padding + 1px border */ 53 | margin-bottom: 20px; 54 | margin-left: -20px; 55 | } 56 | .nav-sidebar > li > a { 57 | padding-right: 20px; 58 | padding-left: 20px; 59 | } 60 | .nav-sidebar > .active > a, 61 | .nav-sidebar > .active > a:hover, 62 | .nav-sidebar > .active > a:focus { 63 | color: #fff; 64 | background-color: #428bca; 65 | } 66 | 67 | /* 68 | * Main content 69 | */ 70 | .main { 71 | padding: 20px; 72 | } 73 | @media (min-width: 768px) { 74 | .main { 75 | padding-right: 40px; 76 | padding-left: 40px; 77 | } 78 | } 79 | .main .page-header { 80 | margin-top: 0; 81 | } 82 | .footer > .container { 83 | padding-top: 20px; 84 | text-align: center; 85 | } 86 | 87 | /* 88 | * Row Links 89 | */ 90 | .table.rowlink td:not(.rowlink-skip), 91 | .table .rowlink td:not(.rowlink-skip) { 92 | cursor: pointer; 93 | } 94 | .table.rowlink td:not(.rowlink-skip) a, 95 | .table .rowlink td:not(.rowlink-skip) a { 96 | font: inherit; 97 | color: inherit; 98 | text-decoration: inherit; 99 | } 100 | .multi-row-description { 101 | border-left: 1px solid #DDD; 102 | } 103 | 104 | pre { 105 | max-height: 40em; 106 | } 107 | 108 | /* shift the anchors to accommodate fixed navbar. See https://github.com/twbs/bootstrap/issues/1768#issuecomment-46519033 */ 109 | *[id]:before { 110 | display: block; 111 | content: " "; 112 | margin-top: -70px; 113 | height: 70px; 114 | visibility: hidden; 115 | } 116 | -------------------------------------------------------------------------------- /enterprise-granny-core/src/main/java/com/sap/hana/cloud/samples/granny/model/ValidationError.java: -------------------------------------------------------------------------------- 1 | package com.sap.hana.cloud.samples.granny.model; 2 | 3 | import java.io.Serializable; 4 | import java.util.Map; 5 | 6 | import javax.xml.bind.annotation.XmlAccessType; 7 | import javax.xml.bind.annotation.XmlAccessorType; 8 | import javax.xml.bind.annotation.XmlRootElement; 9 | 10 | /** 11 | * Object used to report information about validation errors. 12 | * 13 | */ 14 | @XmlRootElement(name = "error") 15 | @XmlAccessorType(XmlAccessType.FIELD) 16 | public class ValidationError implements Serializable 17 | { 18 | /** 19 | * The serialVersionUID of the class. 20 | */ 21 | private static final long serialVersionUID = 1L; 22 | 23 | String messageKey = null; 24 | 25 | String message = null; 26 | 27 | String messageTemplate = null; 28 | 29 | String path = null; 30 | 31 | String invalidValue = null; 32 | 33 | Map messageParameter = null; 34 | 35 | public ValidationError() {} 36 | 37 | public ValidationError(String messageKey, String message, String messageTemplate, String path, String invalidValue, Map parameter) 38 | { 39 | this.setMessageKey(messageKey); 40 | this.setMessage(message); 41 | this.setMessageTemplate(messageTemplate); 42 | this.setPath(path); 43 | this.setInvalidValue(invalidValue); 44 | this.setMessageParameter(parameter); 45 | 46 | } 47 | 48 | 49 | public Map getMessageParameter() 50 | { 51 | return messageParameter; 52 | } 53 | 54 | public void setMessageParameter(Map messageParameter) 55 | { 56 | this.messageParameter = messageParameter; 57 | } 58 | 59 | public String getMessage() 60 | { 61 | return message; 62 | } 63 | 64 | public void setMessage(String message) 65 | { 66 | this.message = message; 67 | } 68 | 69 | public String getMessageTemplate() 70 | { 71 | return messageTemplate; 72 | } 73 | 74 | public void setMessageTemplate(String messageTemplate) 75 | { 76 | this.messageTemplate = messageTemplate; 77 | } 78 | 79 | public String getPath() 80 | { 81 | return path; 82 | } 83 | 84 | public void setPath(String path) 85 | { 86 | this.path = path; 87 | } 88 | 89 | public String getInvalidValue() 90 | { 91 | return invalidValue; 92 | } 93 | 94 | public void setInvalidValue(String invalidValue) 95 | { 96 | this.invalidValue = invalidValue; 97 | } 98 | 99 | public String getMessageKey() 100 | { 101 | return messageKey; 102 | } 103 | 104 | public void setMessageKey(String messageKey) 105 | { 106 | this.messageKey = messageKey; 107 | } 108 | } -------------------------------------------------------------------------------- /enterprise-granny-client/src/main/java/com/sap/hana/cloud/samples/grannyv/client/web/util/ValidationExceptionMapper.java: -------------------------------------------------------------------------------- 1 | package com.sap.hana.cloud.samples.grannyv.client.web.util; 2 | 3 | import java.util.Arrays; 4 | import java.util.Locale; 5 | 6 | import javax.validation.ConstraintViolation; 7 | import javax.validation.ConstraintViolationException; 8 | import javax.validation.ValidationException; 9 | import javax.ws.rs.core.Response; 10 | import javax.ws.rs.ext.ExceptionMapper; 11 | import javax.ws.rs.ext.Provider; 12 | 13 | import org.slf4j.Logger; 14 | import org.slf4j.LoggerFactory; 15 | import org.springframework.context.i18n.LocaleContextHolder; 16 | 17 | import com.sap.hana.cloud.samples.granny.model.StatusMessage; 18 | import com.sap.hana.cloud.samples.granny.model.ValidationError; 19 | import com.sap.hana.cloud.samples.granny.util.ConstraintViolationMapper; 20 | 21 | /** 22 | * Deals with {@link ConstraintViolationException}s intercepted in the inbound RESTful service communication 23 | * and maps the contained {@link ConstraintViolation} to respective {@link ValidationError}s stored within 24 | * a {@link StatusMessage}. 25 | * 26 | * @see org.apache.cxf.jaxrs.validation.JAXRSBeanValidationInInterceptor 27 | * @see StatusMessage 28 | */ 29 | @Provider 30 | public class ValidationExceptionMapper extends ConstraintViolationMapper implements ExceptionMapper 31 | { 32 | Logger log = LoggerFactory.getLogger(ValidationExceptionMapper.class); 33 | 34 | /** 35 | * Catches a {@link ValidationException} and in case its an instance of 36 | * {@link ConstraintViolationException} it maps the contained {@link ConstraintViolation} 37 | * to respective {@link ValidationError}s stored within a {@link StatusMessage}. 38 | * 39 | * @param exception The caught {@link ValidationException} 40 | * @return The {@link Response} containing a respective {@link StatusMessage} 41 | * @see StatusMessage 42 | */ 43 | @Override 44 | public Response toResponse(ValidationException exception) 45 | { 46 | if (exception instanceof ConstraintViolationException) 47 | { 48 | 49 | final ConstraintViolationException constraint = (ConstraintViolationException) exception; 50 | 51 | // get locale 52 | final Locale locale = LocaleContextHolder.getLocale(); 53 | 54 | // convert constraint violation 55 | ValidationError[] errors = convertConstraintViolationExceptionToValidationErrors(constraint, locale, getResourceBundleName()); 56 | 57 | StatusMessage msg = getDefaultStatusMessage(); 58 | msg.setErrors(Arrays.asList(errors)); 59 | 60 | return Response.status(Response.Status.BAD_REQUEST).entity(msg).build(); 61 | 62 | } 63 | else 64 | { 65 | return Response.status(Response.Status.INTERNAL_SERVER_ERROR).build(); 66 | } 67 | 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /enterprise-granny-core/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4.0.0 3 | 4 | com.sap.hana.cloud.samples 5 | enterprise-granny 6 | 2.3.2-BUILD-SNAPSHOT 7 | 8 | 9 | enterprise-granny-core 10 | enterprise-granny-core 11 | Contains the domain model and API interfaces 12 | 13 | 14 | 15 | org.apache.commons 16 | commons-lang3 17 | 3.1 18 | 19 | 20 | 21 | 22 | javax.ws.rs 23 | jsr311-api 24 | ${javax.ws.rs.jsr311-api-version} 25 | 26 | 27 | 28 | 29 | javax.validation 30 | validation-api 31 | 1.1.0.Final 32 | 33 | 34 | 35 | 36 | org.slf4j 37 | slf4j-api 38 | ${org.slf4j-version} 39 | 40 | 41 | 42 | 43 | 44 | 45 | maven-compiler-plugin 46 | 2.5.1 47 | 48 | ${java-version} 49 | ${java-version} 50 | true 51 | true 52 | 53 | 54 | 55 | org.apache.maven.plugins 56 | maven-resources-plugin 57 | 2.5 58 | 59 | UTF-8 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | org.apache.maven.plugins 69 | maven-dependency-plugin 70 | 2.10 71 | 72 | 73 | analyze 74 | 75 | analyze 76 | 77 | 78 | 79 | 80 | false 81 | 82 | 83 | 84 | 85 | 86 | 87 | -------------------------------------------------------------------------------- /enterprise-granny-service/src/main/java/com/osintegrators/example/HomeController.java: -------------------------------------------------------------------------------- 1 | package com.osintegrators.example; 2 | 3 | import org.slf4j.Logger; 4 | import org.slf4j.LoggerFactory; 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.http.HttpStatus; 7 | import org.springframework.stereotype.Controller; 8 | import org.springframework.ui.Model; 9 | import org.springframework.web.bind.annotation.PathVariable; 10 | import org.springframework.web.bind.annotation.RequestBody; 11 | import org.springframework.web.bind.annotation.RequestMapping; 12 | import org.springframework.web.bind.annotation.RequestMethod; 13 | import org.springframework.web.bind.annotation.ResponseBody; 14 | import org.springframework.web.bind.annotation.ResponseStatus; 15 | 16 | import java.util.List; 17 | import java.util.Locale; 18 | 19 | /** 20 | * Handles requests for the application home page. 21 | * 22 | * @deprecated Please use {@link com.sap.hana.cloud.samples.granny.web.ContactController} instead 23 | */ 24 | @Controller 25 | public class HomeController { 26 | 27 | private static final Logger logger = LoggerFactory 28 | .getLogger(HomeController.class); 29 | 30 | @Autowired 31 | AddressService addressService; 32 | 33 | @RequestMapping(value = "/", method = RequestMethod.GET) 34 | public String home(Locale locale, Model model) { 35 | logger.debug("in home method"); 36 | 37 | return "home"; 38 | } 39 | 40 | 41 | @RequestMapping(value = "/get/{_id}", method = RequestMethod.GET) 42 | public @ResponseBody Address get(@PathVariable Long _id) { 43 | logger.debug("in get method"); 44 | return addressService.getAddressById(_id); 45 | } 46 | 47 | @RequestMapping(value = "/create", method = RequestMethod.POST) 48 | @ResponseStatus(HttpStatus.CREATED) 49 | public void update(@RequestBody Address address) { 50 | logger.debug("in create method"); 51 | 52 | addressService.createAddress(address); 53 | 54 | } 55 | 56 | 57 | @RequestMapping(value = "/update", method = RequestMethod.PUT) 58 | @ResponseStatus(HttpStatus.CREATED) 59 | public void create(@RequestBody Address address) { 60 | logger.debug("in create method"); 61 | 62 | addressService.createAddress(address); 63 | 64 | } 65 | 66 | @RequestMapping(value = "/delete", method = RequestMethod.DELETE, consumes = "application/json") 67 | @ResponseStatus(HttpStatus.OK) 68 | public void delete(@RequestBody Address address) { 69 | logger.debug("in delete method"); 70 | 71 | addressService.deleteAddress(address); 72 | } 73 | 74 | @RequestMapping(value = "/addresses", method = RequestMethod.GET) 75 | @ResponseStatus(HttpStatus.OK) 76 | public @ResponseBody List
list() { 77 | logger.debug("in create method"); 78 | 79 | return addressService.getAllAddresses(); 80 | } 81 | 82 | } 83 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | com.sap.hana.cloud.samples 6 | enterprise-granny 7 | enterprise-granny 8 | pom 9 | 2.3.2-BUILD-SNAPSHOT 10 | A sample address book application demonstrating the interplay of popular open-source libraries. [WIP] 11 | 12 | 13 | 1.7 14 | UTF-8 15 | 16 | 4.2.5.RELEASE 17 | 1.11.4.RELEASE 18 | 1.9.4.RELEASE 19 | 20 | 3.1.5 21 | 1.9.13 22 | 1.1.1 23 | 2.5.0 24 | 1.8.7 25 | 2.2 26 | 2.2.2 27 | 28 | 1.7.13 29 | 1.2.16 30 | 31 | 1.2.2.RELEASE 32 | 1.0.4.RELEASE 33 | 34 | 1.92.21.3 35 | 36 | 37 | 38 | SAP SE 39 | http://www.sap.com 40 | 41 | 42 | 43 | 44 | SAP HANA Cloud Platform 45 | http://hcp.sap.com 46 | SAP SE 47 | http://www.sap.com/ 48 | 49 | 50 | 51 | 52 | 53 | The Apache Software License, Version 2.0 54 | http://www.apache.org/licenses/LICENSE-2.0.txt 55 | repo 56 | 57 | 58 | 59 | https://github.com/SAP/cloud-enterprise-granny 60 | 61 | enterprise-granny-core 62 | enterprise-granny-service 63 | enterprise-granny-client 64 | enterprise-granny-phonelib 65 | 66 | -------------------------------------------------------------------------------- /enterprise-granny-client/src/main/webapp/WEB-INF/spring/appServlet/servlet-context.xml: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 12 | 13 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | /WEB-INF/tiles-defs.xml 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /enterprise-granny-service/src/main/java/com/sap/hana/cloud/samples/granny/srv/ContactServiceImpl.java: -------------------------------------------------------------------------------- 1 | package com.sap.hana.cloud.samples.granny.srv; 2 | 3 | import java.util.List; 4 | 5 | import javax.validation.Valid; 6 | 7 | import org.slf4j.Logger; 8 | import org.slf4j.LoggerFactory; 9 | import org.springframework.beans.factory.annotation.Autowired; 10 | import org.springframework.context.annotation.Primary; 11 | import org.springframework.stereotype.Service; 12 | 13 | import com.sap.hana.cloud.samples.granny.dao.ContactDAO; 14 | import com.sap.hana.cloud.samples.granny.model.Contact; 15 | 16 | /** 17 | * Default implementation of the {@link ContactService} interface. 18 | */ 19 | @Service 20 | @Primary 21 | public class ContactServiceImpl extends BaseService implements ContactService 22 | { 23 | /** 24 | * The {@link Logger} to be used. Declared here so that it shows up in logging console early on. 25 | */ 26 | @SuppressWarnings("unused") 27 | private static final Logger logger = LoggerFactory.getLogger(ContactServiceImpl.class); 28 | 29 | /** 30 | * The {@link ContactDAO} used for persistence operations. 31 | */ 32 | @Autowired 33 | ContactDAO contactDAO; 34 | 35 | /** 36 | * {@inheritDoc} 37 | */ 38 | public Contact createContact(@Valid Contact contact) throws ServiceException 39 | { 40 | return this.saveContact(contact); 41 | } 42 | 43 | /** 44 | * {@inheritDoc} 45 | */ 46 | public Contact saveContact(@Valid Contact contact) throws ServiceException 47 | { 48 | Contact retVal = null; 49 | 50 | try 51 | { 52 | retVal = contactDAO.save(contact); 53 | } 54 | catch (Exception ex) 55 | { 56 | this.handleException(ex); 57 | } 58 | 59 | return retVal; 60 | } 61 | 62 | /** 63 | * {@inheritDoc} 64 | */ 65 | public List getAllContactes() throws ServiceException 66 | { 67 | List retVal = null; 68 | 69 | try 70 | { 71 | retVal = contactDAO.findAll(); 72 | } 73 | catch (Exception ex) 74 | { 75 | this.handleException(ex); 76 | } 77 | 78 | return retVal; 79 | } 80 | 81 | /** 82 | * {@inheritDoc} 83 | */ 84 | public void deleteContact(Contact contact) throws ServiceException 85 | { 86 | try 87 | { 88 | contactDAO.delete(contact); 89 | } 90 | catch (Exception ex) 91 | { 92 | this.handleException(ex); 93 | } 94 | } 95 | 96 | /** 97 | * {@inheritDoc} 98 | */ 99 | public Contact getContactById(String id) throws ServiceException 100 | { 101 | Contact retVal = new Contact(); 102 | 103 | try 104 | { 105 | retVal = contactDAO.findOne(id); 106 | } 107 | catch (Exception ex) 108 | { 109 | this.handleException(ex); 110 | } 111 | 112 | return retVal; 113 | } 114 | 115 | /** 116 | * {@inheritDoc} 117 | */ 118 | public Contact updateContact(@Valid Contact contact) throws ServiceException 119 | { 120 | return this.saveContact(contact); 121 | } 122 | } -------------------------------------------------------------------------------- /enterprise-granny-core/src/main/java/com/sap/hana/cloud/samples/granny/model/Address.java: -------------------------------------------------------------------------------- 1 | package com.sap.hana.cloud.samples.granny.model; 2 | 3 | import java.io.Serializable; 4 | 5 | import javax.validation.constraints.Size; 6 | 7 | import org.apache.commons.lang3.builder.ToStringBuilder; 8 | 9 | /** 10 | * Address of a {@link Contact}. 11 | */ 12 | 13 | public class Address extends BaseObject implements Serializable 14 | { 15 | /** 16 | * The serialVersionUID of the class. 17 | */ 18 | private static final long serialVersionUID = 1L; 19 | 20 | protected AddressType type = null; 21 | 22 | @Size(max = 30, message = "{api.data_validation.max_length.error}") 23 | protected String street = null; 24 | 25 | @Size(max = 30, message = "{api.data_validation.max_length.error}") 26 | protected String street2 = null; 27 | 28 | @Size(max = 30, message = "{api.data_validation.max_length.error}") 29 | protected String city = null; 30 | 31 | @Size(max = 8, message = "{api.data_validation.max_length.error}") 32 | protected String zipCode = null; 33 | 34 | @Size(max = 20, message = "{api.data_validation.max_length.error}") 35 | protected String state = null; 36 | 37 | @Size(max = 3, message = "{api.data_validation.max_length.error}") 38 | protected String country = null; 39 | 40 | public AddressType getType() 41 | { 42 | return type; 43 | } 44 | 45 | public void setType(AddressType type) 46 | { 47 | this.type = type; 48 | } 49 | 50 | public String getStreet() 51 | { 52 | return street; 53 | } 54 | 55 | public void setStreet(String street) 56 | { 57 | this.street = street; 58 | } 59 | 60 | public String getStreet2() 61 | { 62 | return street2; 63 | } 64 | 65 | public void setStreet2(String street2) 66 | { 67 | this.street2 = street2; 68 | } 69 | 70 | public String getCity() 71 | { 72 | return city; 73 | } 74 | 75 | public void setCity(String city) 76 | { 77 | this.city = city; 78 | } 79 | 80 | public String getZipCode() 81 | { 82 | return zipCode; 83 | } 84 | 85 | public void setZipCode(String zipCode) 86 | { 87 | this.zipCode = zipCode; 88 | } 89 | 90 | public String getState() 91 | { 92 | return state; 93 | } 94 | 95 | public void setState(String state) 96 | { 97 | this.state = state; 98 | } 99 | 100 | public String getCountry() 101 | { 102 | return country; 103 | } 104 | 105 | public void setCountry(String country) 106 | { 107 | this.country = country; 108 | } 109 | 110 | /** 111 | * @see java.lang.Object#toString() 112 | */ 113 | public String toString() 114 | { 115 | return new ToStringBuilder(this).appendSuper(super.toString()).append("type", this.type).append("street", this.street).append("street2", this.street2) 116 | .append("zipCode", this.zipCode).append("city", this.city).append("country", this.country) 117 | .append("state", this.state).toString(); 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /enterprise-granny-client/src/main/webapp/WEB-INF/views/about.jsp: -------------------------------------------------------------------------------- 1 | <%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%> 2 | <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> 3 | 4 | 5 | 8 |
9 |
10 | " style="width: 200px; height: 200px;margin-bottom: 10px;" class="center-block" /> 11 |

12 | A sample address book application demonstrating the interplay of popular open-source libraries.

13 | 21 |
22 |
23 |
24 |
25 |

26 | The following media has been used as part of the logo:

27 | 32 |
33 |
34 | 35 | 36 | -------------------------------------------------------------------------------- /enterprise-granny-service/src/main/java/com/sap/hana/cloud/samples/granny/web/util/ValidationExceptionMapper.java: -------------------------------------------------------------------------------- 1 | package com.sap.hana.cloud.samples.granny.web.util; 2 | 3 | import java.util.Arrays; 4 | import java.util.Locale; 5 | 6 | import javax.validation.ConstraintViolation; 7 | import javax.validation.ConstraintViolationException; 8 | import javax.validation.ValidationException; 9 | import javax.ws.rs.core.Response; 10 | import javax.ws.rs.ext.ExceptionMapper; 11 | import javax.ws.rs.ext.Provider; 12 | 13 | import org.apache.cxf.validation.ResponseConstraintViolationException; 14 | import org.slf4j.Logger; 15 | import org.slf4j.LoggerFactory; 16 | import org.springframework.context.i18n.LocaleContextHolder; 17 | 18 | import com.sap.hana.cloud.samples.granny.model.StatusMessage; 19 | import com.sap.hana.cloud.samples.granny.model.ValidationError; 20 | import com.sap.hana.cloud.samples.granny.util.ConstraintViolationMapper; 21 | 22 | /** 23 | * Deals with {@link ConstraintViolationException}s intercepted in the inbound RESTful service communication 24 | * and maps the contained {@link ConstraintViolation} to respective {@link ValidationError}s stored within 25 | * a {@link StatusMessage}. 26 | * 27 | * @see org.apache.cxf.jaxrs.validation.JAXRSBeanValidationInInterceptor 28 | * @see StatusMessage 29 | */ 30 | @Provider 31 | public class ValidationExceptionMapper extends ConstraintViolationMapper implements ExceptionMapper 32 | { 33 | Logger log = LoggerFactory.getLogger(ValidationExceptionMapper.class); 34 | 35 | 36 | /** 37 | * Catches a {@link ValidationException} and in case its an instance of 38 | * {@link ConstraintViolationException} it maps the contained {@link ConstraintViolation} 39 | * to respective {@link ValidationError}s stored within a {@link StatusMessage}. 40 | * 41 | * @param exception The caught {@link ValidationException} 42 | * @return The {@link Response} containing a respective {@link StatusMessage} 43 | * @see StatusMessage 44 | */ 45 | @Override 46 | public Response toResponse(ValidationException exception) 47 | { 48 | if (exception instanceof ConstraintViolationException) 49 | { 50 | 51 | final ConstraintViolationException constraint = (ConstraintViolationException) exception; 52 | final boolean isResponseException = constraint instanceof ResponseConstraintViolationException; 53 | 54 | // get locale 55 | final Locale locale = LocaleContextHolder.getLocale(); 56 | 57 | // convert constraint violation 58 | ValidationError[] errors = convertConstraintViolationExceptionToValidationErrors(constraint, locale, getResourceBundleName()); 59 | 60 | if (isResponseException) 61 | { 62 | return Response.status(Response.Status.INTERNAL_SERVER_ERROR).build(); 63 | } 64 | else 65 | { 66 | StatusMessage msg = getDefaultStatusMessage(); 67 | msg.setErrors(Arrays.asList(errors)); 68 | 69 | return Response.status(Response.Status.BAD_REQUEST).entity(msg).build(); 70 | } 71 | } 72 | else 73 | { 74 | return Response.status(Response.Status.INTERNAL_SERVER_ERROR).build(); 75 | } 76 | 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /enterprise-granny-service/src/test/java/com/sap/hana/cloud/samples/granny/dao/TestContactService.java: -------------------------------------------------------------------------------- 1 | package com.sap.hana.cloud.samples.granny.dao; 2 | 3 | import static org.junit.Assert.assertTrue; 4 | import static org.junit.Assert.fail; 5 | 6 | import java.util.List; 7 | 8 | import org.junit.After; 9 | import org.junit.Before; 10 | import org.junit.Test; 11 | import org.junit.runner.RunWith; 12 | import org.springframework.beans.factory.annotation.Autowired; 13 | import org.springframework.test.context.ActiveProfiles; 14 | import org.springframework.test.context.ContextConfiguration; 15 | import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; 16 | import org.springframework.transaction.annotation.Transactional; 17 | 18 | import com.sap.hana.cloud.samples.granny.model.Contact; 19 | import com.sap.hana.cloud.samples.granny.model.StatusMessage; 20 | import com.sap.hana.cloud.samples.granny.model.ValidationError; 21 | import com.sap.hana.cloud.samples.granny.srv.ContactService; 22 | import com.sap.hana.cloud.samples.granny.srv.DataValidationException; 23 | 24 | /** 25 | * Tests for the {@link ContactService} class. 26 | */ 27 | @ContextConfiguration("classpath:/META-INF/spring/spring-persistence-config.xml") 28 | @RunWith(SpringJUnit4ClassRunner.class) 29 | @ActiveProfiles(profiles = "test") 30 | public class TestContactService 31 | { 32 | @Autowired 33 | ContactService contactSrv = null; 34 | 35 | @Before 36 | public void setUp() throws Exception 37 | { 38 | // nothing needs to be done here 39 | } 40 | 41 | @After 42 | public void tearDown() throws Exception 43 | { 44 | // nothing needs to be done here 45 | } 46 | 47 | /** 48 | * Tests the input validation when creating/updating objects. 49 | * 50 | * TODO - remove data validation from DAO layer and implement it on service layer! 51 | */ 52 | @Test 53 | @Transactional 54 | public void testDataValidation() 55 | { 56 | 57 | Contact contact = new Contact(); 58 | contact.setId(null); 59 | 60 | // try to create 61 | try 62 | { 63 | contact = contactSrv.createContact(contact); 64 | fail("Should not be possible to save entities withour a proper ID!"); 65 | } 66 | catch (DataValidationException ex) 67 | { 68 | StatusMessage msg = ex.getStatusMessage(); 69 | 70 | List errors = msg.getErrors(); 71 | 72 | if (errors != null && errors.size() > 0) 73 | { 74 | ValidationError error = errors.get(0); 75 | assertTrue("Should not be possible to save entities withour a proper ID!", ("model.object.id.not_null.error".equals(error.getMessageKey()))); 76 | } 77 | } 78 | catch (Exception ex) 79 | { 80 | assertTrue("RootCause should be DataValidationException!", (ex instanceof DataValidationException)); 81 | } 82 | 83 | catch (NoSuchMethodError er) 84 | { 85 | // TDOD issues with javax-el not present during Maven build or jUnit tests 86 | er.printStackTrace(); 87 | } 88 | catch (java.lang.NoClassDefFoundError er) 89 | { 90 | // TODO issues with hibernate-validator not present during Maven build or jUnit tests 91 | er.printStackTrace(); 92 | } 93 | 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /enterprise-granny-service/src/main/java/com/sap/hana/cloud/samples/granny/xcc/LoggingAspect.java: -------------------------------------------------------------------------------- 1 | package com.sap.hana.cloud.samples.granny.xcc; 2 | 3 | import java.util.Map; 4 | import java.util.concurrent.ConcurrentHashMap; 5 | import java.util.concurrent.ConcurrentMap; 6 | 7 | import org.aspectj.lang.JoinPoint; 8 | import org.aspectj.lang.ProceedingJoinPoint; 9 | import org.aspectj.lang.annotation.AfterThrowing; 10 | import org.aspectj.lang.annotation.Around; 11 | import org.aspectj.lang.annotation.Aspect; 12 | import org.slf4j.Logger; 13 | import org.slf4j.LoggerFactory; 14 | import org.springframework.stereotype.Component; 15 | 16 | /** 17 | * Provides logging capabilities via aspects. 18 | */ 19 | @Aspect 20 | @Component 21 | public class LoggingAspect 22 | { 23 | /** 24 | * {@link ConcurrentMap} of {@link Logger} instances used. "Cached" for better performance. 25 | */ 26 | private static ConcurrentMap, Logger> LOGGERS = new ConcurrentHashMap, Logger>(); 27 | 28 | /** 29 | * Traces method invocation. 30 | * 31 | * @param joinPoint The intercepted {@link ProceedingJoinPoint} 32 | * @return The original result of the intercepted operation 33 | * @throws Throwable In case of an error 34 | */ 35 | @Around("execution(* com.sap.hana.cloud.samples.granny.srv.*.*(..))") 36 | public Object traceMethodInvocation(ProceedingJoinPoint joinPoint) throws Throwable 37 | { 38 | Object retVal = null; 39 | 40 | Class clazz = joinPoint.getSignature().getDeclaringType(); 41 | Logger logger = getLogger(clazz); 42 | 43 | logger.trace("entering: {}", joinPoint.getSignature().toLongString()); 44 | 45 | // proceed with intercepted call 46 | retVal = joinPoint.proceed(); 47 | 48 | logger.trace("exiting {}", joinPoint.getSignature().toLongString()); 49 | 50 | return retVal; 51 | } 52 | 53 | /** 54 | * Traces a (service) {@link Exception}. 55 | * 56 | * @param joinPoint The intercepted {@link JoinPoint} 57 | * @param ex The {@link Exception} to trace 58 | */ 59 | @AfterThrowing(pointcut="execution(* com.sap.hana.cloud.samples.granny.srv.*.*(..))", throwing="ex") 60 | public void traceException(JoinPoint joinPoint, Exception ex) 61 | { 62 | Class clazz = joinPoint.getSignature().getDeclaringType(); 63 | Logger logger = getLogger(clazz); 64 | 65 | logger.trace(ex.getMessage(), ex); 66 | } 67 | 68 | /** 69 | * Provides the corresponding {@link Logger} for the specified {@link Class}. 70 | * 71 | * This is encapsulated here for better performance via caching the {@link Logger} instances in a {@link Map}. 72 | * May not be needed anymore with optimizations done in slf4j version 1.7.5 73 | * 74 | * @see http://bugzilla.slf4j.org/show_bug.cgi?id=298 75 | * 76 | * @param clazz The {@link Class} for which to return a corresponding {@link Logger} 77 | * @return The corresponding {@link Logger} for the specified {@link Class} 78 | */ 79 | protected static Logger getLogger(Class clazz) 80 | { 81 | Logger retVal = LOGGERS.get(clazz); 82 | 83 | if (retVal == null) 84 | { 85 | retVal = LoggerFactory.getLogger(clazz); 86 | LOGGERS.putIfAbsent(clazz, retVal); 87 | } 88 | 89 | return retVal; 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /doc/20a.md: -------------------------------------------------------------------------------- 1 | ## Granny 2.0 - A new Beginning 2 | 3 | ### Microservices on SAP HANA Cloud Platform 4 | 5 | When talking about the value of cloud the conversation has long shifted from cost reduction (plus moving from capital to operational expenses) to business agility. In order to gain or keep a step ahead of the competition it's vital for companies large and small to be able to quickly roll out new and innovative solutions. As such, unprecedented time-to-market and inherent scaling capabilities are the key strengths associated with cloud these days and within that context the topic that gets the most attention these days is [microservices](http://wikipedia.org/wiki/microservices). 6 | 7 | To those not yet familiar with the term or its general meaning I'd recommend reading the correspondent document written by James Lewis and Martin Fowler: [Microservices](http://martinfowler.com/articles/microservices.html). From a technical point of view the main idea is to break down large(r) software systems into smaller autonomous services that are loosely-coupled and which communicate with each other using remote protocols (in contrast to local in-memory invocation). It's the opposite of a monolithic architecture, which has the tendency to become so complex over time that one can no longer rapidly make changes or develop new features without the risk of unintentionally breaking individual parts or even the whole system altogether! 8 | 9 | ![microservices.jpg](20a_microservices.jpg) 10 | ![12factor_app.jpg](20a_12factor_app.jpg) 11 | 12 | While the whole notion of Microservices is independent from a specific technology by design (!!!), it is easy to spot some shared ideas and concepts with their close kin - [SaaS](https://en.wikipedia.org/wiki/Software_as_a_service). In this context, the concept of a [12-factor app](http://12factor.net/) has become a widely accepted stereotype and architectural guideline for cloud applications and both stress the importance of a respective organisational setup (team owns the entire life-cycle from idea to operation) and [DevOps](https://en.wikipedia.org/wiki/DevOps) (incl. continuous delivery). 13 | 14 | It is easy to see how Microservices and Platform-as-a-Service (PaaS) go hand-in-hand and some go even further calling "_cloud the natural habitant for Microservcies_" (Reference: [Stefan Tilkov, InnoQ](https://www.innoq.com/blog/st/2015/06/dont-start-with-a-monolith/)). Consequently, I was intrigued by the idea to demonstrate how-to develop Microservices on the SAP HANA Cloud Platform during this year's SAP TechEd. 15 | 16 | 17 | + [**Presentation**](http://www.slideshare.net/saphcp/dev300-architecture-guidelines-for-microservices-based-on-sap-hana-cloud-platform) 18 | + [**Recording**](http://events.sap.com/teched/en/session/22720 "http://events.sap.com/teched/en/session/22720") 19 | 20 | The corresponding source code is now the new master branch (2.x) of the **Enterprise Granny** sample application:[https://github.com/SAP/cloud-enterprise-granny](https://github.com/SAP/cloud-enterprise-granny "https://github.com/SAP/cloud-enterprise-granny") 21 | 22 | For all those interested to dig deeper ... the Enterprise Granny series will continue to highlight and discuss key aspects mentioned in the presentation and even go beyond these topics. So, if that's your cup of tea make sure to subscribe to the repo to get notified of changes! 23 | 24 | _Happy coding everyone!_ -------------------------------------------------------------------------------- /enterprise-granny-core/src/main/java/com/sap/hana/cloud/samples/granny/model/Contact.java: -------------------------------------------------------------------------------- 1 | package com.sap.hana.cloud.samples.granny.model; 2 | 3 | import java.io.Serializable; 4 | import java.util.List; 5 | 6 | import javax.validation.Valid; 7 | import javax.validation.constraints.Size; 8 | import javax.xml.bind.annotation.XmlRootElement; 9 | 10 | import org.apache.commons.lang3.builder.ToStringBuilder; 11 | 12 | /** 13 | * A contact person. 14 | */ 15 | @XmlRootElement 16 | public class Contact extends BaseObject implements Serializable 17 | { 18 | /** 19 | * The serialVersionUID of the class. 20 | */ 21 | private static final long serialVersionUID = 1L; 22 | 23 | protected Salutation salutation = null; 24 | 25 | protected Title title = null; 26 | 27 | @Size(max = 30, message = "{api.data_validation.max_length.error}") 28 | protected String firstName = null; 29 | 30 | @Size(max = 30, message = "{api.data_validation.max_length.error}") 31 | protected String lastName = null; 32 | 33 | @Valid 34 | protected List
addresses = null; 35 | 36 | @Valid 37 | protected List phoneNumbers = null; 38 | 39 | @Valid 40 | protected List emailAddresses = null; 41 | 42 | /** 43 | * The salutation 44 | */ 45 | public Salutation getSalutation() 46 | { 47 | return salutation; 48 | } 49 | 50 | public void setSalutation(Salutation salutation) 51 | { 52 | this.salutation = salutation; 53 | } 54 | 55 | /** 56 | * The title 57 | */ 58 | public Title getTitle() 59 | { 60 | return title; 61 | } 62 | 63 | public void setTitle(Title title) 64 | { 65 | this.title = title; 66 | } 67 | 68 | /** 69 | * The first name 70 | */ 71 | public String getFirstName() 72 | { 73 | return firstName; 74 | } 75 | 76 | public void setFirstName(String firstName) 77 | { 78 | this.firstName = firstName; 79 | } 80 | 81 | /* 82 | * The last name 83 | */ 84 | public String getLastName() 85 | { 86 | return lastName; 87 | } 88 | 89 | public void setLastName(String lastName) 90 | { 91 | this.lastName = lastName; 92 | } 93 | 94 | public List
getAddresses() 95 | { 96 | return addresses; 97 | } 98 | 99 | public void setAddresses(List
addresses) 100 | { 101 | this.addresses = addresses; 102 | } 103 | 104 | public List getPhoneNumbers() 105 | { 106 | return phoneNumbers; 107 | } 108 | 109 | public void setPhoneNumbers(List phoneNumbers) 110 | { 111 | this.phoneNumbers = phoneNumbers; 112 | } 113 | 114 | public List getEmailAddresses() 115 | { 116 | return emailAddresses; 117 | } 118 | 119 | public void setEmailAddresses(List emailAdresses) 120 | { 121 | this.emailAddresses = emailAdresses; 122 | } 123 | 124 | /** 125 | * @see java.lang.Object#toString() 126 | */ 127 | public String toString() 128 | { 129 | return new ToStringBuilder(this).appendSuper(super.toString()).append("salutation", this.salutation).append("title", this.title) 130 | .append("firstName", this.firstName).append("lastName", this.lastName).append("addresses", this.addresses) 131 | .append("phoneNumbers", this.phoneNumbers).append("emailAddresses", this.emailAddresses).toString(); 132 | } 133 | 134 | } 135 | -------------------------------------------------------------------------------- /enterprise-granny-client/src/main/webapp/resources/js/respond.min.js: -------------------------------------------------------------------------------- 1 | /*! matchMedia() polyfill - Test a CSS media type/query in JS. Authors & copyright (c) 2012: Scott Jehl, Paul Irish, Nicholas Zakas. Dual MIT/BSD license */ 2 | /*! NOTE: If you're already including a window.matchMedia polyfill via Modernizr or otherwise, you don't need this part */ 3 | window.matchMedia=window.matchMedia||function(a){"use strict";var c,d=a.documentElement,e=d.firstElementChild||d.firstChild,f=a.createElement("body"),g=a.createElement("div");return g.id="mq-test-1",g.style.cssText="position:absolute;top:-100em",f.style.background="none",f.appendChild(g),function(a){return g.innerHTML='­',d.insertBefore(f,e),c=42===g.offsetWidth,d.removeChild(f),{matches:c,media:a}}}(document); 4 | 5 | /*! Respond.js v1.3.0: min/max-width media query polyfill. (c) Scott Jehl. MIT/GPLv2 Lic. j.mp/respondjs */ 6 | (function(a){"use strict";function x(){u(!0)}var b={};if(a.respond=b,b.update=function(){},b.mediaQueriesSupported=a.matchMedia&&a.matchMedia("only all").matches,!b.mediaQueriesSupported){var q,r,t,c=a.document,d=c.documentElement,e=[],f=[],g=[],h={},i=30,j=c.getElementsByTagName("head")[0]||d,k=c.getElementsByTagName("base")[0],l=j.getElementsByTagName("link"),m=[],n=function(){for(var b=0;l.length>b;b++){var c=l[b],d=c.href,e=c.media,f=c.rel&&"stylesheet"===c.rel.toLowerCase();d&&f&&!h[d]&&(c.styleSheet&&c.styleSheet.rawCssText?(p(c.styleSheet.rawCssText,d,e),h[d]=!0):(!/^([a-zA-Z:]*\/\/)/.test(d)&&!k||d.replace(RegExp.$1,"").split("/")[0]===a.location.host)&&m.push({href:d,media:e}))}o()},o=function(){if(m.length){var b=m.shift();v(b.href,function(c){p(c,b.href,b.media),h[b.href]=!0,a.setTimeout(function(){o()},0)})}},p=function(a,b,c){var d=a.match(/@media[^\{]+\{([^\{\}]*\{[^\}\{]*\})+/gi),g=d&&d.length||0;b=b.substring(0,b.lastIndexOf("/"));var h=function(a){return a.replace(/(url\()['"]?([^\/\)'"][^:\)'"]+)['"]?(\))/g,"$1"+b+"$2$3")},i=!g&&c;b.length&&(b+="/"),i&&(g=1);for(var j=0;g>j;j++){var k,l,m,n;i?(k=c,f.push(h(a))):(k=d[j].match(/@media *([^\{]+)\{([\S\s]+?)$/)&&RegExp.$1,f.push(RegExp.$2&&h(RegExp.$2))),m=k.split(","),n=m.length;for(var o=0;n>o;o++)l=m[o],e.push({media:l.split("(")[0].match(/(only\s+)?([a-zA-Z]+)\s?/)&&RegExp.$2||"all",rules:f.length-1,hasquery:l.indexOf("(")>-1,minw:l.match(/\(\s*min\-width\s*:\s*(\s*[0-9\.]+)(px|em)\s*\)/)&&parseFloat(RegExp.$1)+(RegExp.$2||""),maxw:l.match(/\(\s*max\-width\s*:\s*(\s*[0-9\.]+)(px|em)\s*\)/)&&parseFloat(RegExp.$1)+(RegExp.$2||"")})}u()},s=function(){var a,b=c.createElement("div"),e=c.body,f=!1;return b.style.cssText="position:absolute;font-size:1em;width:1em",e||(e=f=c.createElement("body"),e.style.background="none"),e.appendChild(b),d.insertBefore(e,d.firstChild),a=b.offsetWidth,f?d.removeChild(e):e.removeChild(b),a=t=parseFloat(a)},u=function(b){var h="clientWidth",k=d[h],m="CSS1Compat"===c.compatMode&&k||c.body[h]||k,n={},o=l[l.length-1],p=(new Date).getTime();if(b&&q&&i>p-q)return a.clearTimeout(r),r=a.setTimeout(u,i),void 0;q=p;for(var v in e)if(e.hasOwnProperty(v)){var w=e[v],x=w.minw,y=w.maxw,z=null===x,A=null===y,B="em";x&&(x=parseFloat(x)*(x.indexOf(B)>-1?t||s():1)),y&&(y=parseFloat(y)*(y.indexOf(B)>-1?t||s():1)),w.hasquery&&(z&&A||!(z||m>=x)||!(A||y>=m))||(n[w.media]||(n[w.media]=[]),n[w.media].push(f[w.rules]))}for(var C in g)g.hasOwnProperty(C)&&g[C]&&g[C].parentNode===j&&j.removeChild(g[C]);for(var D in n)if(n.hasOwnProperty(D)){var E=c.createElement("style"),F=n[D].join("\n");E.type="text/css",E.media=D,j.insertBefore(E,o.nextSibling),E.styleSheet?E.styleSheet.cssText=F:E.appendChild(c.createTextNode(F)),g.push(E)}},v=function(a,b){var c=w();c&&(c.open("GET",a,!0),c.onreadystatechange=function(){4!==c.readyState||200!==c.status&&304!==c.status||b(c.responseText)},4!==c.readyState&&c.send(null))},w=function(){var b=!1;try{b=new a.XMLHttpRequest}catch(c){b=new a.ActiveXObject("Microsoft.XMLHTTP")}return function(){return b}}();n(),b.update=n,a.addEventListener?a.addEventListener("resize",x,!1):a.attachEvent&&a.attachEvent("onresize",x)}})(this); -------------------------------------------------------------------------------- /doc/01.md: -------------------------------------------------------------------------------- 1 | ## There's no spoon - but a fork! 2 | 3 | ### The Background Story 4 | 5 | Those who closely follow this space may remember the blog posts about 6 | "Granny's Addressbook", a sample application created by a company called [Open 7 | Software Integrators](http://www.osintegrators.com/about) for the purpose of 8 | teaching how-to develop Spring-based Java applications. This sample 9 | applications was used as a reference to compare several PaaS offerings in the 10 | market - see [Which freaking PaaS should I use?](http://www.infoworld.com/d/cloud-computing/which-freaking-paas-should-i-use-204189) 11 | 12 | Unfortunately the SAP HANA Cloud Platform wasn't on their radar back then, but 13 | once we heard about their comparison and the demo app we took our chances and 14 | deployed the app to our cloud platform. It run right away, w/o any 15 | modifications required. [Harald Muller](https://scn.sap.com/people/harald.mueller), Chief Product 16 | Owner of the JVM-based Runtime capabilities of the SAP HANA Cloud Platform, 17 | shared his experiences in a blog post: [Which freaking PaaS should I use 18 | (1/2)?](http://scn.sap.com/community/developer-center/cloud-platform/blog/2012/10/10/which-freaking-paas-should-i-use-12) 19 | 20 | Yet, he did not stop there and instead tinkered with it a bit more to "_make 21 | the app a bit more enterprise ready!_" 22 | 23 | 24 | > _"This time I will not just deploy the applications as is but have a look into the source code and make the app a bit more enterprise ready (hey we are SAP ;-)."_ - Harald Müller [[REF](http://scn.sap.com/community/developer-center/cloud-platform/blog/2012/10/10/which-freaking-paas-should-i-use-12)] 25 | 26 | 27 | Andrew Oliver (the original author) noticed our efforts via Twitter and 28 | responded with a dedicated blog post titled "[The best-run Granny's 29 | addressbooks run on SAP](http://osintegrators.com/SAPknowsGranny)". This is 30 | how the idea of "Enterprise Granny" was born... 31 | 32 | ### Enterprise Granny 33 | 34 | The idea is simple: let's take this simple sample application and make it 35 | enterprise-ready. It's Maven-based and uses the popular Spring framework - a 36 | very common archetype out there - and as such seems like the perfect fit to 37 | showcase how-to port an existing Java application and optimize it for the SAP 38 | HANA Cloud Platform. We'll take it easy at first and apply a few subtle 39 | changes touching upon the most common development tasks like implementing 40 | logging/tracing, exception handling, I18N and so on. Later on, we may want to 41 | enhance the application to demonstrate how-to best leverage the capabilities 42 | of the SAP HANA Cloud Platform. In short: **we'll have plenty of fun with it!** 43 | 44 | **Note:** I'm fully aware that the original intent of the application was purely for training purposes and that some of application's shortcomings are on purpose for educational reasons. Matter of fact, I'm considering to make a few common mistakes myself along the way in order to correct them at a later point in time for the exact same reason! As such, please do not mistake me talking about shortcomings in the original app as criticizing OS Integrators or their develovers! 45 | 46 | ### 47 | 48 | ### There's no spoon - but a fork! 49 | 50 | As you hopefully know we have started to publish a variety of open-source 51 | samples on our github page: [sap.github.io](http://sap.github.io/) or 52 | ([github.com/SAP](https://github.com/SAP/) respectively). Consequently I did 53 | [fork](http://en.wikipedia.org/wiki/Fork_(software_development)) the original 54 | repo (repository) in order to create a new branch for Enterprise Granny. 55 | 56 | You can find it here: [https://github.com/SAP/cloud-enterprise- 57 | granny](https://github.com/SAP/cloud-enterprise-granny) 58 | 59 | Those of you who are interested to follow-up on this journey are more than 60 | welcome - and don't worry: we'll take it easy and enhance the app step-by- 61 | step! After all, the whole purpose of this exercise is to provide you with 62 | some guidance on how-to develop great cloud applications based on Open Source. 63 | 64 | 65 | ### _Happy coding everyone!!!_ 66 | 67 | PS: Of course we all know that there's only one person who can legitimately 68 | call herself "Enterprise Granny", which is the one-and-only [Marilyn 69 | Pratt](http://scn.sap.com/people/marilyn.pratt) (aka **Grannimari**). In more than one way this can be 70 | considered a [hommage](http://en.wikipedia.org/wiki/Hommage) to her and her 71 | ways of sharing with the community and hence I hope she doesn't mind us (re-) 72 | using that title ! ;) 73 | 74 | 75 | -------------------------------------------------------------------------------- /enterprise-granny-client/src/main/webapp/resources/img/icon_9727.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /enterprise-granny-service/src/main/webapp/resources/img/icon_9727.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /enterprise-granny-service/src/main/java/com/sap/hana/cloud/samples/granny/xcc/DataValidationAspect.java: -------------------------------------------------------------------------------- 1 | package com.sap.hana.cloud.samples.granny.xcc; 2 | 3 | import java.lang.annotation.Annotation; 4 | import java.lang.reflect.Method; 5 | import java.util.Locale; 6 | import java.util.Set; 7 | 8 | import javax.inject.Inject; 9 | import javax.validation.ConstraintViolation; 10 | import javax.validation.ConstraintViolationException; 11 | import javax.validation.Valid; 12 | 13 | import org.aspectj.lang.JoinPoint; 14 | import org.aspectj.lang.annotation.Aspect; 15 | import org.aspectj.lang.annotation.Before; 16 | import org.aspectj.lang.reflect.MethodSignature; 17 | import org.springframework.beans.factory.annotation.Value; 18 | import org.springframework.context.i18n.LocaleContextHolder; 19 | import org.springframework.stereotype.Component; 20 | import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean; 21 | 22 | import com.sap.hana.cloud.samples.granny.model.ValidationError; 23 | import com.sap.hana.cloud.samples.granny.srv.DataValidationException; 24 | import com.sap.hana.cloud.samples.granny.util.ConstraintViolationMapper; 25 | 26 | /** 27 | * Ensures data validation based on JSR-303. 28 | * 29 | * @see http://static.springsource.org/spring/docs/3.2.x/spring-framework-reference/html/aop.html 30 | */ 31 | @Aspect 32 | @Component 33 | public class DataValidationAspect 34 | { 35 | 36 | @Inject 37 | LocalValidatorFactoryBean validator = null; 38 | 39 | @Value("${messageResourceBundleName}") 40 | String messageResourceBundleName = null; 41 | 42 | /** 43 | * Validates the data of any method parameter annotated with {@link Valid}. 44 | * 45 | * @param joinPoint The intercepted {@link JoinPoint} 46 | * @throws DataValidationException In case of an validation error 47 | */ 48 | @Before("execution(* com.sap.hana.cloud.samples.granny.srv.*.*(@javax.validation.Valid (*)))") 49 | public void validateIncomingData(JoinPoint joinPoint) throws DataValidationException 50 | { 51 | // get information about the intercepted method 52 | final MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature(); 53 | final Method method = methodSignature.getMethod(); 54 | 55 | // get the annotations and names of the method parameters 56 | final Annotation[][] paramAnnotations = method.getParameterAnnotations(); 57 | final String[] paramNames = methodSignature.getParameterNames(); 58 | 59 | // get the message parameter themselves 60 | final Object[] param = joinPoint.getArgs(); 61 | 62 | if (param == null || param.length < 1) 63 | { 64 | return; // nothing to validate 65 | } 66 | 67 | // check whether or not they have been annotated with @Valid 68 | for (int i = 0; i < param.length; i++) 69 | { 70 | validateParam(paramAnnotations[i], param[i], paramNames[i]); 71 | } 72 | } 73 | 74 | /** 75 | * 76 | * @param annotations 77 | * @param param 78 | * @param paramName 79 | * @throws DataValidationException 80 | */ 81 | void validateParam(Annotation[] annotations, Object param, String paramName) throws DataValidationException 82 | { 83 | // check if parameter needs to be validated 84 | if (! needsValidation(annotations)) 85 | { 86 | return; 87 | } 88 | 89 | try // to validate the parameter 90 | { 91 | 92 | Set> constraints = this.validator.getValidator().validate(param); 93 | 94 | // get locale 95 | final Locale locale = LocaleContextHolder.getLocale(); 96 | 97 | ValidationError[] errors = ConstraintViolationMapper.convertConstraintViolationsToValidationErrors(constraints, locale, this.messageResourceBundleName); 98 | 99 | // only throw data validation exception in case we found errors ;) 100 | if (errors != null && errors.length > 0) 101 | { 102 | throw new DataValidationException(errors); 103 | } 104 | } 105 | catch (ConstraintViolationException ex) 106 | { 107 | throw new DataValidationException(ex); 108 | } 109 | } 110 | 111 | /** 112 | * 113 | * @param annotations 114 | * @return 115 | */ 116 | private boolean needsValidation(Annotation[] annotations) 117 | { 118 | for (Annotation annotation : annotations) 119 | { 120 | if (Valid.class.isInstance(annotation)) 121 | { 122 | return true; 123 | } 124 | } 125 | 126 | return false; 127 | } 128 | 129 | public String getMessageResourceBundleName() 130 | { 131 | return messageResourceBundleName; 132 | } 133 | 134 | public void setMessageResourceBundleName(String messageResourceBundleName) 135 | { 136 | this.messageResourceBundleName = messageResourceBundleName; 137 | } 138 | 139 | } 140 | -------------------------------------------------------------------------------- /enterprise-granny-service/src/main/resources/META-INF/spring/spring-persistence-config.xml: -------------------------------------------------------------------------------- 1 | 2 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | false 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | i18n/messages 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | ${messageResourceBundleName} 68 | 69 | 70 | 71 | 72 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | -------------------------------------------------------------------------------- /enterprise-granny-client/src/main/java/com/sap/hana/cloud/samples/granny/client/AddressbookServiceFactory.java: -------------------------------------------------------------------------------- 1 | package com.sap.hana.cloud.samples.granny.client; 2 | 3 | import java.net.URISyntaxException; 4 | import java.util.ArrayList; 5 | import java.util.List; 6 | 7 | import javax.naming.Context; 8 | import javax.naming.InitialContext; 9 | import javax.naming.NamingException; 10 | 11 | import org.apache.cxf.jaxrs.client.JAXRSClientFactory; 12 | import org.codehaus.jackson.jaxrs.JacksonJaxbJsonProvider; 13 | import org.codehaus.jackson.jaxrs.JacksonJsonProvider; 14 | import org.slf4j.Logger; 15 | import org.slf4j.LoggerFactory; 16 | 17 | import com.sap.core.connectivity.api.http.HttpDestination; 18 | import com.sap.hana.cloud.samples.granny.api.ContactFacade; 19 | 20 | public class AddressbookServiceFactory 21 | { 22 | private static final Logger logger = LoggerFactory.getLogger(AddressbookServiceFactory.class); 23 | 24 | /** 25 | * Name of the logical destination of the Addressbook service to be used. 26 | * Should match the resource name specified in the web.xml. 27 | */ 28 | final static String DESTINATION_NAME = "Addressbook-Service"; 29 | 30 | /** 31 | * The HTTP URL of the Addressbook service to be used. 32 | */ 33 | final static String endPoint = getEndPoint(); 34 | 35 | /** 36 | * Returns the URI of the Addressbook service to be used. 37 | * 38 | * @return URI of the Addressbook service to be used 39 | */ 40 | static String getEndPoint() 41 | { 42 | String retVal = null; 43 | 44 | String destinationName = DESTINATION_NAME; 45 | 46 | final String landscape = System.getenv("HC_LANDSCAPE"); // NEO 47 | final String vcap = System.getenv("VCAP_APPLICATION"); // Cloud Foundry 48 | 49 | if (landscape == null && vcap == null) 50 | { 51 | // no landscape indicator found, assume we are running locally 52 | retVal = "http://localhost:8080/api/v1"; 53 | } 54 | else 55 | { 56 | if (landscape != null) // NEO 57 | { 58 | retVal = getAddressbookServiceDestination(destinationName); 59 | } 60 | 61 | if (vcap != null) // Cloud Foundry 62 | { 63 | destinationName = DESTINATION_NAME.toUpperCase().replace('-', '_'); 64 | retVal = System.getenv(destinationName); 65 | } 66 | } 67 | 68 | if (retVal == null) 69 | { 70 | logger.error("No API destination with name '{}‘ found!", destinationName); 71 | } 72 | else 73 | { 74 | if (logger.isInfoEnabled()) 75 | { 76 | logger.info("API destination: " + retVal); 77 | } 78 | } 79 | 80 | return retVal; 81 | } 82 | 83 | /** 84 | * Returns the URI of the Addressbook service to be used via the corresponding 85 | * destination of the Connectivity service or NULL if no valid 86 | * destination was found. 87 | * 88 | * @param destinationName The name of the {@link HttpDestination} to be used 89 | * @return URI of the Addressbook service to be used via the corresponding 90 | * destination of the Connectivity service or NULL if no valid 91 | * destination was found 92 | * 93 | * @see https://help.hana.ondemand.com/help/frameset.htm?e5c9867dbb571014957ef9d7a8846b1c.html 94 | */ 95 | static String getAddressbookServiceDestination(String destinationName) 96 | { 97 | String retVal = null; 98 | 99 | try 100 | { 101 | Context ctx = new InitialContext(); 102 | 103 | HttpDestination destination = null; 104 | 105 | destination = (HttpDestination) ctx.lookup("java:comp/env/" + destinationName); 106 | 107 | if (destination != null && destination.getURI() != null) 108 | { 109 | retVal = destination.getURI().toASCIIString(); 110 | } 111 | } 112 | catch (NamingException ex) 113 | { 114 | logger.error("No API destination with name '{}‘ found!", destinationName); 115 | } 116 | catch (URISyntaxException ex) 117 | { 118 | logger.error("Invalid URI defined for API destination with name '{}‘!", destinationName); } 119 | 120 | return retVal; 121 | 122 | } 123 | 124 | /** 125 | * Returns a reference to the Addressbook service (proxy) to be used. 126 | * 127 | * @return A reference to the Addressbook service (proxy) to be used 128 | */ 129 | public ContactFacade getService() 130 | { 131 | ContactFacade retVal = null; 132 | 133 | // set up providers 134 | List providers = new ArrayList(); 135 | JacksonJaxbJsonProvider jsonProvider = new JacksonJaxbJsonProvider(); 136 | providers.add(jsonProvider); 137 | 138 | if (endPoint != null) 139 | { 140 | 141 | // initialize proxy 142 | retVal = JAXRSClientFactory.create(endPoint, ContactFacade.class, providers); 143 | } 144 | 145 | return retVal; 146 | } 147 | } 148 | -------------------------------------------------------------------------------- /enterprise-granny-core/src/main/java/com/sap/hana/cloud/samples/granny/model/StatusMessage.java: -------------------------------------------------------------------------------- 1 | package com.sap.hana.cloud.samples.granny.model; 2 | 3 | import java.io.Serializable; 4 | import java.util.Arrays; 5 | import java.util.List; 6 | 7 | import javax.xml.bind.annotation.XmlAccessType; 8 | import javax.xml.bind.annotation.XmlAccessorType; 9 | import javax.xml.bind.annotation.XmlElement; 10 | import javax.xml.bind.annotation.XmlRootElement; 11 | 12 | import org.apache.commons.lang3.builder.EqualsBuilder; 13 | import org.apache.commons.lang3.builder.HashCodeBuilder; 14 | import org.apache.commons.lang3.builder.ToStringBuilder; 15 | import org.apache.commons.lang3.builder.ToStringStyle; 16 | 17 | /** 18 | * Object used to report (error) messages. 19 | * 20 | */ 21 | @XmlRootElement(name = "status") 22 | @XmlAccessorType(XmlAccessType.FIELD) 23 | public class StatusMessage implements Serializable, Cloneable 24 | { 25 | /** 26 | * The serialVersionUID of the class. 27 | */ 28 | private static final long serialVersionUID = 1L; 29 | 30 | int code = 500; 31 | 32 | String error = null; 33 | 34 | @XmlElement(name="error_description") 35 | String description = null; 36 | 37 | String message = null; 38 | 39 | List errors = null; 40 | 41 | public StatusMessage(){} 42 | 43 | public StatusMessage(int code, String error, String description, String message, ValidationError...errors) 44 | { 45 | this.setCode(code); 46 | this.setError(error); 47 | this.setDescription(description); 48 | this.setMessage(message); 49 | 50 | if (errors != null) 51 | { 52 | this.setErrors(Arrays.asList(errors)); 53 | } 54 | } 55 | 56 | public StatusMessage(String error, String description, String message, ValidationError...errors) 57 | { 58 | this.setError(error); 59 | this.setDescription(description); 60 | this.setMessage(message); 61 | 62 | if (errors != null) 63 | { 64 | this.setErrors(Arrays.asList(errors)); 65 | } 66 | } 67 | 68 | 69 | public List getErrors() 70 | { 71 | return errors; 72 | } 73 | 74 | public void setErrors(List errors) 75 | { 76 | this.errors = errors; 77 | } 78 | 79 | public int getCode() 80 | { 81 | return code; 82 | } 83 | 84 | public void setCode(int code) 85 | { 86 | this.code = code; 87 | } 88 | 89 | public String getError() 90 | { 91 | return error; 92 | } 93 | 94 | public void setError(String error) 95 | { 96 | this.error = error; 97 | } 98 | 99 | public String getDescription() 100 | { 101 | return description; 102 | } 103 | 104 | public void setDescription(String description) 105 | { 106 | this.description = description; 107 | } 108 | 109 | public String getMessage() 110 | { 111 | return message; 112 | } 113 | 114 | public void setMessage(String message) 115 | { 116 | this.message = message; 117 | } 118 | 119 | /** 120 | * @see java.lang.Object#toString() 121 | */ 122 | public String toString() 123 | { 124 | return new ToStringBuilder(this, ToStringStyle.DEFAULT_STYLE).appendSuper(super.toString()) 125 | .append("error", this.error).append("message", this.message).append("description", this.description) 126 | .append("code", this.code).toString(); 127 | } 128 | 129 | /** 130 | * @see java.lang.Object#hashCode() 131 | */ 132 | public int hashCode() 133 | { 134 | return new HashCodeBuilder(-950597061, 209594933).appendSuper(super.hashCode()).append(this.message) 135 | .append(this.error).append(this.description).append(this.code).toHashCode(); 136 | } 137 | 138 | /** 139 | * @see java.lang.Object#equals(Object) 140 | */ 141 | public boolean equals(Object object) 142 | { 143 | if (object == this) 144 | { 145 | return true; 146 | } 147 | 148 | if (!(object instanceof StatusMessage)) 149 | { 150 | return false; 151 | } 152 | 153 | StatusMessage rhs = (StatusMessage) object; 154 | 155 | return new EqualsBuilder().appendSuper(super.equals(object)).append(this.message, rhs.message) 156 | .append(this.error, rhs.error).append(this.description, rhs.description).append(this.code, rhs.code) 157 | .isEquals(); 158 | } 159 | 160 | /* 161 | * (non-Javadoc) 162 | * @see java.lang.Object#clone() 163 | */ 164 | public StatusMessage clone() 165 | { 166 | StatusMessage retVal = new StatusMessage(); 167 | 168 | retVal.setCode(this.getCode()); 169 | 170 | retVal.description = (this.getDescription() == null) ? null : new String(this.getDescription()); 171 | retVal.error = (this.getError() == null) ? null : new String(this.getError()); 172 | retVal.message = (this.getMessage() == null) ? null : new String(this.getMessage()); 173 | 174 | return retVal; 175 | } 176 | 177 | } 178 | -------------------------------------------------------------------------------- /enterprise-granny-service/src/main/webapp/admin/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Addressbook Service 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 |
48 | 63 | 64 |
65 | 73 | 74 |
75 |
76 | 79 |

80 | Stay tuned! ;) 81 |

82 | 83 |
84 |
85 |
86 |
REST API
87 |
88 | 92 |
93 |
94 |
95 |
96 |
97 |
98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 110 | 111 | 112 | 116 | 117 | -------------------------------------------------------------------------------- /enterprise-granny-phonelib/src/main/java/com/sap/hana/cloud/samples/granny/libphonenumber/LibPhonenumberService.java: -------------------------------------------------------------------------------- 1 | package com.sap.hana.cloud.samples.granny.libphonenumber; 2 | 3 | import java.util.Arrays; 4 | import java.util.Locale; 5 | 6 | import javax.ws.rs.Consumes; 7 | import javax.ws.rs.GET; 8 | import javax.ws.rs.Path; 9 | import javax.ws.rs.Produces; 10 | import javax.ws.rs.QueryParam; 11 | import javax.ws.rs.core.Context; 12 | import javax.ws.rs.core.HttpHeaders; 13 | import javax.ws.rs.core.MediaType; 14 | import javax.ws.rs.core.Response; 15 | import javax.ws.rs.core.Response.Status; 16 | 17 | import org.springframework.stereotype.Service; 18 | 19 | import com.google.i18n.phonenumbers.NumberParseException; 20 | import com.google.i18n.phonenumbers.PhoneNumberToCarrierMapper; 21 | import com.google.i18n.phonenumbers.PhoneNumberUtil; 22 | import com.google.i18n.phonenumbers.PhoneNumberUtil.PhoneNumberFormat; 23 | import com.google.i18n.phonenumbers.PhoneNumberUtil.PhoneNumberType; 24 | import com.google.i18n.phonenumbers.Phonenumber.PhoneNumber; 25 | 26 | /** 27 | * Service for validating & formatting phone numbers. 28 | * 29 | * Basically an HTTP-based wrapper around the Google I18N libphonenumber library. 30 | * 31 | * @see https://github.com/googlei18n/libphonenumber 32 | */ 33 | @Service("libPhonenumberService") 34 | @Path("/phone") 35 | @Produces({ "application/json" }) 36 | @com.webcohesion.enunciate.metadata.Facet(value="PhoneNumber Service", documentation="Provides a microservice for Google's libphonenumber library.") 37 | @com.webcohesion.enunciate.metadata.rs.ResourceLabel(value="PhoneNumber Service") 38 | @com.webcohesion.enunciate.metadata.rs.ServiceContextRoot(value="/api/v1") 39 | public class LibPhonenumberService 40 | { 41 | /** 42 | * Validates the specified phone number taking into account the specified region code. 43 | * 44 | * @param phoneNumber The phone number to validate 45 | * @param region Region Code string using ISO 3166-1 two-letter country-code format in upper-case. 46 | * @return The validation result 47 | * 48 | * @see The list of the codes can be found here: http://www.iso.org/iso/country_codes/iso_3166_code_lists/country_names_and_code_elements.htm 49 | */ 50 | @GET 51 | @Consumes(MediaType.APPLICATION_JSON) 52 | @com.webcohesion.enunciate.metadata.rs.TypeHint(PhoneNumberValidationResult.class) 53 | @com.webcohesion.enunciate.metadata.rs.StatusCodes(value = { 54 | @com.webcohesion.enunciate.metadata.rs.ResponseCode(code = 200, condition = "In case of success"), 55 | @com.webcohesion.enunciate.metadata.rs.ResponseCode(code = 400, condition = "In case no phonenumber or region code was provided")}) 56 | 57 | public Response validate(@QueryParam("phonenumber") String phoneNumber, 58 | @QueryParam("region") String region, 59 | @Context HttpHeaders header) 60 | { 61 | PhoneNumberUtil phoneUtil = PhoneNumberUtil.getInstance(); 62 | 63 | PhoneNumber phoneNo = null; 64 | 65 | PhoneNumberValidationResult result = new PhoneNumberValidationResult(); 66 | result.setNumber(phoneNumber); 67 | 68 | try 69 | { 70 | boolean knownCountry = false; 71 | String country = null; 72 | 73 | if (region != null && ! region.isEmpty()) 74 | { 75 | country = region.toUpperCase(); 76 | } 77 | else 78 | { 79 | if (header.getLanguage() != null) 80 | { 81 | country = header.getLanguage().getCountry(); 82 | } 83 | } 84 | 85 | if (country != null) 86 | { 87 | knownCountry = Arrays.asList(Locale.getISOCountries()).contains(country); 88 | } 89 | else 90 | { 91 | return Response.status(Status.BAD_REQUEST).build(); 92 | } 93 | 94 | if (! knownCountry) 95 | { 96 | // fallback 97 | } 98 | 99 | phoneNo = phoneUtil.parse(phoneNumber, country); 100 | 101 | // check for validity 102 | boolean isValid = phoneUtil.isValidNumber(phoneNo); 103 | 104 | 105 | if (isValid) 106 | { 107 | PhoneNumberType type = phoneUtil.getNumberType(phoneNo); 108 | PhoneNumberToCarrierMapper carrierMapper = PhoneNumberToCarrierMapper.getInstance(); 109 | 110 | String intFormat = phoneUtil.format(phoneNo, PhoneNumberFormat.INTERNATIONAL); 111 | String phoneNoType = type.toString().toUpperCase(); 112 | String carrier = carrierMapper.getNameForNumber(phoneNo, Locale.ENGLISH); 113 | 114 | result.setValid(isValid); 115 | result.setNumber(intFormat); 116 | result.setType(phoneNoType); 117 | result.setCarrier(carrier); 118 | } 119 | } 120 | catch (NumberParseException e) 121 | { 122 | System.err.println("NumberParseException was thrown: " + e.toString()); 123 | return Response.status(Status.BAD_REQUEST).entity(e.toString()).build(); 124 | } 125 | 126 | return Response.ok(result).build(); 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /enterprise-granny-core/src/main/java/com/sap/hana/cloud/samples/granny/model/BaseObject.java: -------------------------------------------------------------------------------- 1 | package com.sap.hana.cloud.samples.granny.model; 2 | 3 | import java.util.Date; 4 | import java.util.UUID; 5 | 6 | import javax.validation.constraints.NotNull; 7 | import javax.validation.constraints.Size; 8 | 9 | import org.apache.commons.lang3.builder.ToStringBuilder; 10 | 11 | 12 | /** 13 | * Base class for all domain model objects. 14 | */ 15 | public abstract class BaseObject 16 | { 17 | 18 | /** 19 | * The (globally unique) ID of the object. 20 | */ 21 | @NotNull(message = "{model.object.id.not_null.error}") 22 | @Size(max = 36, message = "{api.data_validation.max_length.error}") 23 | private String id = UUID.randomUUID().toString(); 24 | 25 | /** 26 | * The {@link Date} the object was created at. 27 | */ 28 | private Date createdAt = null; 29 | 30 | /** 31 | * The {@link Date} the object was last modified at. 32 | */ 33 | private Date lastModifiedAt = null; 34 | 35 | /** 36 | * ID of the user who created the object. 37 | */ 38 | @Size(max = 20, message = "{api.data_validation.max_length.error}") 39 | private String createdBy = null; 40 | 41 | /** 42 | * ID of the user who was the last to modify the object. 43 | */ 44 | @Size(max = 20, message = "{api.data_validation.max_length.error}") 45 | private String lastModifiedBy = null; 46 | 47 | /** 48 | * The version number used for optimistic locking. 49 | * 50 | * @see http://en.wikibooks.org/wiki/Java_Persistence/Locking 51 | * @see http://eclipse.org/eclipselink/documentation/2.4/jpa/extensions/a_optimisticlocking.htm 52 | */ 53 | private Long version = 0L; 54 | 55 | /** 56 | * Life-cycle event callback, which automatically sets the last modification date. 57 | */ 58 | protected void updateAuditInformation() 59 | { 60 | lastModifiedAt = new Date(); 61 | 62 | // TODO - obtain currently logged-on user 63 | } 64 | 65 | /** 66 | * Life-cycle event callback, which automatically creates a unique ID for the object 67 | * and populates its audit information. 68 | */ 69 | protected void generateAuditInformation() 70 | { 71 | final Date now = new Date(); 72 | 73 | createdAt = now; 74 | lastModifiedAt = now; 75 | 76 | // TODO - obtain currently logged-on user 77 | } 78 | 79 | public String getId() 80 | { 81 | return this.id; 82 | } 83 | 84 | public void setId(String id) 85 | { 86 | this.id = id; 87 | } 88 | 89 | public Date getCreatedAt() 90 | { 91 | return this.createdAt; 92 | } 93 | 94 | public void setCreatedAt(Date createdAt) 95 | { 96 | this.createdAt = createdAt; 97 | } 98 | 99 | public Date getLastModifiedAt() 100 | { 101 | return this.lastModifiedAt; 102 | } 103 | 104 | public void setLastModifiedAt(Date lastModifiedAt) 105 | { 106 | this.lastModifiedAt = lastModifiedAt; 107 | } 108 | 109 | public String getCreatedBy() 110 | { 111 | return this.createdBy; 112 | } 113 | 114 | public void setCreatedBy(String createdBy) 115 | { 116 | this.createdBy = createdBy; 117 | } 118 | 119 | public String getLastModifiedBy() 120 | { 121 | return this.lastModifiedBy; 122 | } 123 | 124 | public void setLastModifiedBy(String lastModifiedBy) 125 | { 126 | this.lastModifiedBy = lastModifiedBy; 127 | } 128 | 129 | public Long getVersion() 130 | { 131 | return version; 132 | } 133 | 134 | public void setVersion(Long version) 135 | { 136 | this.version = version; 137 | } 138 | 139 | /** 140 | * @see java.lang.Object#toString() 141 | */ 142 | public String toString() 143 | { 144 | return new ToStringBuilder(this).append("id", this.id).append("createdAt", this.createdAt).append("createdBy", this.createdBy) 145 | .append("lastModifiedAt", this.lastModifiedAt).append("lastModifiedBy", this.lastModifiedBy).append("version", this.version).toString(); 146 | } 147 | 148 | /** 149 | * @see java.lang.Object#equals(Object) 150 | */ 151 | public final boolean equals(Object obj) 152 | { 153 | if (this == obj) 154 | { 155 | return true; 156 | } 157 | 158 | if (obj == null || !(obj instanceof BaseObject)) 159 | { 160 | return false; 161 | } 162 | 163 | BaseObject other = (BaseObject) obj; 164 | 165 | if (id == null) 166 | { 167 | return false; 168 | } 169 | 170 | return id.equals(other.getId()); 171 | } 172 | 173 | /** 174 | * @see java.lang.Object#hashCode() 175 | */ 176 | public final int hashCode() 177 | { 178 | if (id != null) 179 | { 180 | return id.hashCode(); 181 | } 182 | else 183 | { 184 | return super.hashCode(); 185 | } 186 | } 187 | } 188 | -------------------------------------------------------------------------------- /enterprise-granny-phonelib/src/main/webapp/WEB-INF/spring/spring-context.xml.bak: -------------------------------------------------------------------------------- 1 | 2 | 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 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | --------------------------------------------------------------------------------