11package org .reujdon .jtp .client ;
22
33import org .reujdon .jtp .client .commands .Command ;
4- import org .reujdon .jtp .shared .PropertiesUtil ;
54import org .reujdon .jtp .shared .json .JsonException ;
65import org .reujdon .jtp .shared .messaging .Message ;
76import org .reujdon .jtp .shared .messaging .MessageFactory ;
3635public class JTPClient implements Runnable , AutoCloseable {
3736 private static final Logger logger = LoggerFactory .getLogger (JTPClient .class );
3837
39- // TODO: abstract config logic to be reused in server
40- // Constants for environment variable keys
41- private static final String ENV_HOST = "CLIENT_HOST" ;
42- private static final String ENV_PORT = "CLIENT_PORT" ;
43- private static final String ENV_TRUSTSTORE_PATH = "CLIENT_TRUSTSTORE_PATH" ;
44- private static final String ENV_TRUSTSTORE_PASSWORD = "CLIENT_TRUSTSTORE_PASSWORD" ;
45- private static final String ENV_API_KEY = "CLIENT_API_KEY" ;
46- private static final String DEFAULT_CONFIG_FILE = "client.properties" ;
47-
48- private String host ;
49- private int port = -1 ;
50- private String apiKey ;
51- private String truststorePath ;
52- private String truststorePassword ;
38+ private final JTPClientConfig config ;
5339
5440 private SSLSocket sslSocket ;
5541
@@ -82,112 +68,8 @@ public class JTPClient implements Runnable, AutoCloseable {
8268 * @see #JTPClient()
8369 */
8470 public JTPClient (String configFile ) {
85- loadConfig (configFile );
86- }
87-
88- /**
89- * Loads configuration from environment and properties file.
90- *
91- * @param configFile path to properties file
92- * @see #loadFromEnvVars()
93- * @see #loadFromPropertiesFile(String)
94- */
95- private void loadConfig (String configFile ){
96- loadFromEnvVars ();
97-
98- if (hasMissingConfig ()) {
99- if (configFile == null )
100- configFile = DEFAULT_CONFIG_FILE ;
101-
102- loadFromPropertiesFile (configFile );
103- }
104-
105- validateConfig ();
106- }
107-
108- /**
109- * Checks for missing required configuration.
110- *
111- * @return true if any required config is missing
112- */
113- private boolean hasMissingConfig () {
114- return host == null || port == -1 || apiKey == null || truststorePath == null || truststorePassword == null ;
115- }
116-
117- /**
118- * Loads configuration from environment variables.
119- */
120- private void loadFromEnvVars () {
121- String envHost = System .getenv (ENV_HOST );
122- if (envHost != null && !envHost .isBlank ())
123- this .host = envHost .trim ();
124-
125- String envApiKey = System .getenv (ENV_API_KEY );
126- if (envApiKey != null && !envApiKey .isBlank ())
127- this .apiKey = envApiKey .trim ();
128-
129- String envPort = System .getenv (ENV_PORT );
130- if (envPort != null ) {
131- try {
132- this .port = Integer .parseInt (envPort );
133- } catch (NumberFormatException e ) {
134- throw new IllegalArgumentException ("Invalid PORT in env vars" , e );
135- }
136- }
137-
138- String envTruststorePath = System .getenv (ENV_TRUSTSTORE_PATH );
139- if (envTruststorePath != null )
140- this .truststorePath = envTruststorePath ;
141-
142- String envTruststorePassword = System .getenv (ENV_TRUSTSTORE_PASSWORD );
143- if (envTruststorePassword != null )
144- this .truststorePassword = envTruststorePassword ;
145- }
146-
147- /**
148- * Loads configuration from properties file.
149- *
150- * @param configFile path to properties file
151- * @throws IllegalArgumentException if file is invalid
152- */
153- private void loadFromPropertiesFile (String configFile ) {
154- if (this .host == null )
155- this .host = PropertiesUtil .getString (configFile , "client.host" );
156-
157- if (this .port == -1 )
158- this .port = PropertiesUtil .getInteger (configFile , "client.port" );
159-
160- if (this .apiKey == null )
161- this .apiKey = PropertiesUtil .getString (configFile , "client.apiKey" );
162-
163- if (this .truststorePath == null )
164- this .truststorePath = PropertiesUtil .getString (configFile , "client.path" );
165-
166- if (this .truststorePassword == null )
167- this .truststorePassword = PropertiesUtil .getString (configFile , "client.password" );
168-
169- }
170-
171- /**
172- * Validates loaded configuration.
173- *
174- * @throws IllegalArgumentException if any config is invalid
175- */
176- private void validateConfig () {
177- if (this .host == null || this .host .isBlank ())
178- throw new IllegalArgumentException ("Host must be set via " + ENV_HOST + " or properties file" );
179-
180- if (this .port < 0 || this .port > 65536 )
181- throw new IllegalArgumentException ("PORT must be between 0 and 65536 and set via " + ENV_PORT + " or properties file" );
182-
183- if (this .apiKey == null || this .apiKey .isBlank ())
184- throw new IllegalArgumentException ("API key must be set via " + ENV_API_KEY + " or properties file" );
185-
186- if (this .truststorePath == null || this .truststorePath .isBlank ())
187- logger .warn ("Truststore path not set" );
188-
189- if (this .truststorePath != null && this .truststorePassword == null )
190- throw new IllegalArgumentException ("Truststore password must be set via " + ENV_TRUSTSTORE_PASSWORD + " or properties file" );
71+ this .config = new JTPClientConfig ();
72+ config .loadConfig (configFile , "client.properties" );
19173 }
19274
19375 /**
@@ -201,14 +83,14 @@ public void run() {
20183 try {
20284 SSLContext sslContext = createSSLContext ();
20385 SSLSocketFactory sslSocketFactory = sslContext .getSocketFactory ();
204- sslSocket = (SSLSocket ) sslSocketFactory .createSocket (host , port );
86+ sslSocket = (SSLSocket ) sslSocketFactory .createSocket (config . host , config . port );
20587
20688 sslSocket .setEnabledCipherSuites (sslSocket .getSupportedCipherSuites ());
20789
20890 in = new BufferedReader (new InputStreamReader (sslSocket .getInputStream ()));
20991 out = new PrintWriter (sslSocket .getOutputStream (), true );
21092
211- logger .info ("Connected to server at {} : {}\n " , host , port );
93+ logger .info ("Connected to server at {} : {}\n " , config . host , config . port );
21294
21395 running = true ;
21496
@@ -238,19 +120,19 @@ public void run() {
238120 */
239121 private SSLContext createSSLContext () {
240122 try {
241- if (truststorePath == null || truststorePath .trim (). isEmpty ()) {
123+ if (config . truststorePath == null || config . truststorePath .isBlank ()) {
242124 logger .warn ("No truststore configured - using default SSLContext with standard certificate validation" );
243125 return SSLContext .getDefault ();
244126 }
245127
246- File truststoreFile = new File (truststorePath );
128+ File truststoreFile = new File (config . truststorePath );
247129 if (!truststoreFile .exists ())
248- throw new FileNotFoundException ("Truststore file not found at: " + truststorePath );
130+ throw new FileNotFoundException ("Truststore file not found at: " + config . truststorePath );
249131
250- try (FileInputStream fis = new FileInputStream (truststorePath )) {
132+ try (FileInputStream fis = new FileInputStream (config . truststorePath )) {
251133 // Load the truststore
252134 KeyStore trustStore = KeyStore .getInstance ("JKS" );
253- trustStore .load (fis , truststorePassword .toCharArray ());
135+ trustStore .load (fis , config . truststorePassword .toCharArray ());
254136
255137 // Initialize trust manager factory
256138 TrustManagerFactory trustManagerFactory = TrustManagerFactory .getInstance (TrustManagerFactory .getDefaultAlgorithm ());
@@ -273,7 +155,7 @@ private SSLContext createSSLContext() {
273155 private void sendAuth () {
274156 logger .info ("Client authenticating..." );
275157
276- Auth auth = new Auth (apiKey );
158+ Auth auth = new Auth (config . apiKey );
277159 out .println (auth .toJSON ());
278160 out .flush ();
279161 }
@@ -399,7 +281,7 @@ public void close() {
399281 private void waitForPendingCommands () {
400282 logger .info ("Waiting for pending responses to complete..." );
401283
402- final int maxWaitMs = 5000 ;
284+ final int maxWaitMs = config . shutdownTimeout ;
403285 final int checkIntervalMs = 100 ;
404286 final long endTime = System .currentTimeMillis () + maxWaitMs ;
405287
0 commit comments