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.client;
018
019import java.util.concurrent.ExecutorService;
020import java.util.concurrent.Executors;
021import tech.aroma.client.exceptions.BananaException;
022import tech.aroma.thrift.application.service.ApplicationServiceConstants;
023import tech.aroma.thrift.authentication.ApplicationToken;
024import tech.aroma.thrift.endpoint.Endpoint;
025import tech.aroma.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 Aroma
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 Aroma create()
064    {
065        return create("Aroma");
066    }
067    
068    static Aroma create(@NonEmpty String applicationToken)
069    {
070        checkThat(applicationToken)
071            .usingMessage("Application Token cannot be empty")
072            .is(nonEmptyString());
073        
074        return newBuilder()
075            .withAsyncExecutorService(Executors.newSingleThreadExecutor())
076            .withApplicationToken(applicationToken)
077            .build();
078    }
079    
080    static Builder newBuilder()
081    {
082        return new Builder();
083    }
084    
085    @BuilderPattern(role = BUILDER)
086    static final class Builder 
087    {
088        
089        private String hostname = ApplicationServiceConstants.PRODUCTION_ENDPOINT.getHostname();
090        private int port = ApplicationServiceConstants.PRODUCTION_ENDPOINT.getPort();
091        private String applicationToken = "";
092        private ExecutorService async;
093        
094        Builder() 
095        {
096            
097        }
098        
099        /**
100         * Set the Token ID created from the Aroma App.
101         * 
102         * @param applicationToken
103         * @return
104         * 
105         * @throws IllegalArgumentException 
106         */
107        public Builder withApplicationToken(@Required String applicationToken) throws IllegalArgumentException
108        {
109            checkThat(applicationToken)
110                .are(nonEmptyString());
111            
112            this.applicationToken = applicationToken;
113            
114            return this;
115        }
116        
117        public Builder withEndpoint(@NonEmpty String hostname, int port) throws IllegalArgumentException
118        {
119            checkThat(hostname)
120                .usingMessage("hostname cannot be empty")
121                .is(nonEmptyString());
122            
123            checkThat(port)
124                .is(validPort());
125                
126            this.hostname = hostname;
127            this.port = port;
128            
129            return this;
130        }
131        
132        public Builder withAsyncExecutorService(@Required ExecutorService executor) throws IllegalArgumentException
133        {
134            checkThat(executor)
135                .is(notNull());
136            
137            this.async = executor;
138            
139            return this;
140        }
141        
142        public Aroma build() throws IllegalStateException
143        {
144            checkThat(hostname)
145                .throwing(IllegalStateException.class)
146                .usingMessage("missing hostname")
147                .is(nonEmptyString());
148            
149            checkThat(applicationToken)
150                .throwing(IllegalStateException.class)
151                .usingMessage("missing Application Token")
152                .is(nonEmptyString());
153            
154            checkThat(port)
155                .throwing(IllegalStateException.class)
156                .is(validPort());
157            
158            if (async == null)
159            {
160                async = Executors.newSingleThreadExecutor();
161            }
162            
163            Endpoint endpoint = createEndpoint();
164            
165            ApplicationToken token = new ApplicationToken().setTokenId(applicationToken);
166            
167            ThriftClientProvider clientProvider = new ThriftClientProvider(() -> endpoint);
168            AromaClient banana = new AromaClient(() -> clientProvider.get(), async, token);
169            return banana;
170            
171        }
172
173        private Endpoint createEndpoint()
174        {
175            TcpEndpoint tcpEndpoint = new TcpEndpoint(hostname, port);
176
177            Endpoint endpoint = new Endpoint();
178            endpoint.setTcp(tcpEndpoint);
179            return endpoint;
180        }
181        
182    }
183    
184}