001/* 002 * Copyright 2015 Aroma Tech. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package tech.aroma.banana.client; 018 019import java.util.concurrent.ExecutorService; 020import java.util.concurrent.Executors; 021import tech.aroma.banana.client.exceptions.BananaException; 022import tech.aroma.banana.thrift.application.service.ApplicationServiceConstants; 023import tech.aroma.banana.thrift.authentication.ApplicationToken; 024import tech.aroma.banana.thrift.endpoint.Endpoint; 025import tech.aroma.banana.thrift.endpoint.TcpEndpoint; 026import tech.sirwellington.alchemy.annotations.arguments.NonEmpty; 027import tech.sirwellington.alchemy.annotations.arguments.Optional; 028import tech.sirwellington.alchemy.annotations.arguments.Required; 029import tech.sirwellington.alchemy.annotations.concurrency.ThreadSafe; 030import tech.sirwellington.alchemy.annotations.designs.FluidAPIDesign; 031import tech.sirwellington.alchemy.annotations.designs.patterns.BuilderPattern; 032 033import static tech.sirwellington.alchemy.annotations.designs.patterns.BuilderPattern.Role.BUILDER; 034import static tech.sirwellington.alchemy.annotations.designs.patterns.BuilderPattern.Role.PRODUCT; 035import static tech.sirwellington.alchemy.arguments.Arguments.checkThat; 036import static tech.sirwellington.alchemy.arguments.assertions.Assertions.notNull; 037import static tech.sirwellington.alchemy.arguments.assertions.NetworkAssertions.validPort; 038import static tech.sirwellington.alchemy.arguments.assertions.StringAssertions.nonEmptyString; 039 040/** 041 * 042 * @author SirWellington 043 */ 044@ThreadSafe 045@BuilderPattern(role = PRODUCT) 046@FluidAPIDesign 047public interface Banana 048{ 049 050 Request begin(); 051 052 interface Request 053 { 054 Request text(@Required String message, @Optional Object...args); 055 056 Request titled(@Required String title); 057 058 Request withUrgency(@Required Urgency level) throws IllegalArgumentException; 059 060 void send() throws IllegalArgumentException, BananaException; 061 } 062 063 static Banana create() 064 { 065 return newBuilder() 066 .withAsyncExecutorService(Executors.newSingleThreadExecutor()) 067 .withApplicationToken("Banana") 068 .build(); 069 } 070 071 static Builder newBuilder() 072 { 073 return new Builder(); 074 } 075 076 @BuilderPattern(role = BUILDER) 077 static final class Builder 078 { 079 080 private String hostname = ApplicationServiceConstants.PRODUCTION_ENDPOINT.getHostname(); 081 private int port = ApplicationServiceConstants.PRODUCTION_ENDPOINT.getPort(); 082 private String applicationToken = ""; 083 private ExecutorService async; 084 085 Builder() 086 { 087 088 } 089 090 /** 091 * Set the Token ID created from the Aroma App. 092 * 093 * @param applicationToken 094 * @return 095 * 096 * @throws IllegalArgumentException 097 */ 098 public Builder withApplicationToken(@Required String applicationToken) throws IllegalArgumentException 099 { 100 checkThat(applicationToken) 101 .are(nonEmptyString()); 102 103 this.applicationToken = applicationToken; 104 105 return this; 106 } 107 108 public Builder withEndpoint(@NonEmpty String hostname, int port) throws IllegalArgumentException 109 { 110 checkThat(hostname) 111 .usingMessage("hostname cannot be empty") 112 .is(nonEmptyString()); 113 114 checkThat(port) 115 .is(validPort()); 116 117 this.hostname = hostname; 118 this.port = port; 119 120 return this; 121 } 122 123 public Builder withAsyncExecutorService(@Required ExecutorService executor) throws IllegalArgumentException 124 { 125 checkThat(executor) 126 .is(notNull()); 127 128 this.async = executor; 129 130 return this; 131 } 132 133 public Banana build() throws IllegalStateException 134 { 135 checkThat(hostname) 136 .throwing(IllegalStateException.class) 137 .usingMessage("missing hostname") 138 .is(nonEmptyString()); 139 140 checkThat(applicationToken) 141 .throwing(IllegalStateException.class) 142 .usingMessage("missing Application Token") 143 .is(nonEmptyString()); 144 145 checkThat(port) 146 .throwing(IllegalStateException.class) 147 .is(validPort()); 148 149 if (async == null) 150 { 151 async = Executors.newSingleThreadExecutor(); 152 } 153 154 Endpoint endpoint = createEndpoint(); 155 156 ApplicationToken token = new ApplicationToken().setTokenId(applicationToken); 157 158 ThriftClientProvider clientProvider = new ThriftClientProvider(() -> endpoint); 159 BananaClient banana = new BananaClient(() -> clientProvider.get(), async, token); 160 return banana; 161 162 } 163 164 private Endpoint createEndpoint() 165 { 166 TcpEndpoint tcpEndpoint = new TcpEndpoint(hostname, port); 167 168 Endpoint endpoint = new Endpoint(); 169 endpoint.setTcp(tcpEndpoint); 170 return endpoint; 171 } 172 173 } 174 175}