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}