View Javadoc
1   /*
2    * junixsocket
3    *
4    * Copyright 2009-2024 Christian Kohlschütter
5    *
6    * Licensed under the Apache License, Version 2.0 (the "License");
7    * you may not use this file except in compliance with the License.
8    * You may obtain a copy of the License at
9    *
10   *     http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing, software
13   * distributed under the License is distributed on an "AS IS" BASIS,
14   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15   * See the License for the specific language governing permissions and
16   * limitations under the License.
17   */
18  package org.newsclub.net.unix.demo;
19  
20  import java.io.File;
21  import java.io.IOException;
22  import java.net.InetSocketAddress;
23  import java.net.Socket;
24  import java.net.SocketAddress;
25  import java.net.URI;
26  import java.util.Objects;
27  import java.util.Properties;
28  import java.util.function.Function;
29  
30  import org.newsclub.net.unix.AFSocketAddress;
31  import org.newsclub.net.unix.AFUNIXSocket;
32  import org.newsclub.net.unix.AFUNIXSocketAddress;
33  
34  import com.kohlschutter.annotations.compiletime.ExcludeFromCodeCoverageGeneratedReport;
35  
36  /**
37   * Just a helper class to simplify controlling the demo from the command line.
38   */
39  public final class DemoHelper {
40    @ExcludeFromCodeCoverageGeneratedReport(reason = "unreachable")
41    private DemoHelper() {
42      throw new IllegalStateException("No instances");
43    }
44  
45    /**
46     * Adds a key-value pair to a Properties instance. Takes values from a given system property and
47     * overrides the default value with it.
48     *
49     * @param props The Properties instance to write to.
50     * @param key The name of the property.
51     * @param defaultValue The default value (for demo purposes)
52     * @param property The name of the system property that can override the default value.
53     * @param exampleValue An example value that is different from the default.
54     */
55    public static void addProperty(Properties props, String key, String defaultValue, String property,
56        String exampleValue) {
57      String value = defaultValue;
58      if (property == null) {
59        System.out.println(key + "=" + value);
60      } else {
61        value = System.getProperty(property, value);
62        String example;
63        if (exampleValue == null) {
64          example = "";
65        } else {
66          if (exampleValue.endsWith(")")) {
67            example = "=" + exampleValue;
68          } else {
69            example = "=" + exampleValue + " (for example)";
70          }
71        }
72        System.out.println(key + "=" + value + " -- override with -D" + property + example);
73      }
74      props.setProperty(key, value);
75    }
76  
77    public static void initJDBCDriverClass(String property, String defaultValue, String exampleValue)
78        throws ClassNotFoundException {
79  
80      if (exampleValue == null) {
81        exampleValue = "(...)";
82      } else {
83        if (!exampleValue.endsWith(")")) {
84          exampleValue += " (for example)";
85        }
86      }
87  
88      String driverClass = System.getProperty(property, defaultValue);
89      if (driverClass.isEmpty()) {
90        System.out.println("Using JDBC driver provided by SPI -- override with -D" + property + "="
91            + exampleValue);
92      } else {
93        if (driverClass.equals(defaultValue)) {
94          System.out.println("Using JDBC default driver " + driverClass + " -- override with -D"
95              + property + "=" + exampleValue);
96        } else {
97          System.out.println("Using JDBC driver provided by -D" + property + "=" + driverClass);
98        }
99        Class.forName(driverClass);
100     }
101   }
102 
103   public static String getPropertyValue(String property, String defaultValue, String exampleValue) {
104     return getPropertyValue(property, property, defaultValue, exampleValue, null);
105   }
106 
107   public static <R> R getPropertyValue(String property, String defaultValue, String exampleValue,
108       Function<String, R> valueConverter) {
109     return getPropertyValue(property, property, defaultValue, exampleValue, valueConverter);
110   }
111 
112   @SuppressWarnings("unchecked")
113   public static <R> R getPropertyValue(String variable, String property, String defaultValue,
114       String exampleValue, Function<String, R> valueConverter) {
115     boolean print = true;
116     if (exampleValue == null) {
117       print = false;
118     } else if (exampleValue.isEmpty()) {
119       exampleValue = "(...)";
120     } else {
121       if (exampleValue.contains("$")) {
122         exampleValue = "'" + exampleValue + "'";
123       }
124       if (!exampleValue.endsWith(")")) {
125         exampleValue += " (for example)";
126       }
127     }
128 
129     String value = System.getProperty(property, defaultValue);
130 
131     if (print) {
132       final String overrideOrSet;
133       final String valueString;
134       if (value == null) {
135         valueString = "(not set)";
136         overrideOrSet = "set";
137       } else {
138         valueString = value;
139         overrideOrSet = "override";
140       }
141 
142       if (Objects.equals(defaultValue, exampleValue)) {
143         System.out.println(variable + "=" + valueString + " -- " + overrideOrSet + " with -D"
144             + property + "=" + "(...)");
145       } else {
146         System.out.println(variable + "=" + valueString + " -- " + overrideOrSet + " with -D"
147             + property + "=" + exampleValue);
148       }
149     }
150 
151     R returnValue;
152     if (valueConverter != null) {
153       returnValue = valueConverter.apply(value);
154     } else {
155       returnValue = (R) value;
156     }
157 
158     return returnValue;
159   }
160 
161   public static SocketAddress socketAddress(String socketName) throws IOException {
162     if (socketName.startsWith("file:")) {
163       // demo only: assume file: URLs are always handled by AFUNIXSocketAddress
164       return AFUNIXSocketAddress.of(URI.create(socketName));
165     } else if (socketName.contains(":/")) {
166       // assume URI, e.g., unix:// or tipc://
167       return AFSocketAddress.of(URI.create(socketName));
168     }
169 
170     int colon = socketName.lastIndexOf(':');
171     int slashOrBackslash = Math.max(socketName.lastIndexOf('/'), socketName.lastIndexOf('\\'));
172 
173     if (socketName.startsWith("@")) {
174       // abstract namespace (Linux only!)
175       return AFUNIXSocketAddress.inAbstractNamespace(socketName.substring(1));
176     } else if (colon > 0 && slashOrBackslash < colon && !socketName.startsWith("/")) {
177       // assume TCP socket
178       String hostname = socketName.substring(0, colon);
179       int port = Integer.parseInt(socketName.substring(colon + 1));
180       return new InetSocketAddress(hostname, port);
181     } else {
182       // assume unix socket file name
183       return AFUNIXSocketAddress.of(new File(socketName));
184     }
185   }
186 
187   public static Socket connectSocket(SocketAddress socketAddress) throws IOException {
188     if (socketAddress instanceof AFUNIXSocketAddress) {
189       return AFUNIXSocket.connectTo((AFUNIXSocketAddress) socketAddress);
190     } else {
191       Socket socket = new Socket();
192       socket.connect(socketAddress);
193       return socket;
194     }
195   }
196 
197   public static SocketAddress parseAddress(String[] args, SocketAddress defaultAddress)
198       throws IOException {
199     if (args.length == 0) {
200       return defaultAddress;
201     } else if (args.length == 1) {
202       return parseAddress("--unix", args[0], defaultAddress);
203     } else {
204       return parseAddress(args[0], args[1], defaultAddress);
205     }
206   }
207 
208   public static SocketAddress parseAddress(String opt, String val, SocketAddress defaultAddress)
209       throws IOException {
210     if (opt == null || val == null) {
211       return defaultAddress;
212     }
213     switch (opt) {
214       case "--unix":
215         return AFUNIXSocketAddress.of(new File(val));
216       case "--url":
217         return AFSocketAddress.of(URI.create(val));
218       default:
219         throw new IllegalArgumentException("Valid parameters: --unix <path> OR --url <URL>");
220     }
221   }
222 }