View Javadoc
1   /*
2    * junixsocket
3    *
4    * Copyright 2009-2026 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  import com.kohlschutter.annotations.compiletime.SuppressFBWarnings;
36  
37  /**
38   * Just a helper class to simplify controlling the demo from the command line.
39   */
40  @SuppressFBWarnings({"UNENCRYPTED_SOCKET", "PATH_TRAVERSAL_IN"})
41  public final class DemoHelper {
42    @ExcludeFromCodeCoverageGeneratedReport(reason = "unreachable")
43    private DemoHelper() {
44      throw new IllegalStateException("No instances");
45    }
46  
47    /**
48     * Adds a key-value pair to a Properties instance. Takes values from a given system property and
49     * overrides the default value with it.
50     *
51     * @param props The Properties instance to write to.
52     * @param key The name of the property.
53     * @param defaultValue The default value (for demo purposes)
54     * @param property The name of the system property that can override the default value.
55     * @param exampleValue An example value that is different from the default.
56     */
57    public static void addProperty(Properties props, String key, String defaultValue, String property,
58        String exampleValue) {
59      String value = defaultValue;
60      if (property == null) {
61        System.out.println(key + "=" + value);
62      } else {
63        value = System.getProperty(property, value);
64        String example;
65        if (exampleValue == null) {
66          example = "";
67        } else {
68          if (exampleValue.endsWith(")")) {
69            example = "=" + exampleValue;
70          } else {
71            example = "=" + exampleValue + " (for example)";
72          }
73        }
74        System.out.println(key + "=" + value + " -- override with -D" + property + example);
75      }
76      props.setProperty(key, value);
77    }
78  
79    public static void initJDBCDriverClass(String property, String defaultValue, String exampleValue)
80        throws ClassNotFoundException {
81  
82      if (exampleValue == null) {
83        exampleValue = "(...)";
84      } else {
85        if (!exampleValue.endsWith(")")) {
86          exampleValue += " (for example)";
87        }
88      }
89  
90      String driverClass = System.getProperty(property, defaultValue);
91      if (driverClass.isEmpty()) {
92        System.out.println("Using JDBC driver provided by SPI -- override with -D" + property + "="
93            + exampleValue);
94      } else {
95        if (driverClass.equals(defaultValue)) {
96          System.out.println("Using JDBC default driver " + driverClass + " -- override with -D"
97              + property + "=" + exampleValue);
98        } else {
99          System.out.println("Using JDBC driver provided by -D" + property + "=" + driverClass);
100       }
101       Class.forName(driverClass);
102     }
103   }
104 
105   public static String getPropertyValue(String property, String defaultValue, String exampleValue) {
106     return getPropertyValue(property, property, defaultValue, exampleValue, null);
107   }
108 
109   public static <R> R getPropertyValue(String property, String defaultValue, String exampleValue,
110       Function<String, R> valueConverter) {
111     return getPropertyValue(property, property, defaultValue, exampleValue, valueConverter);
112   }
113 
114   @SuppressWarnings("unchecked")
115   public static <R> R getPropertyValue(String variable, String property, String defaultValue,
116       String exampleValue, Function<String, R> valueConverter) {
117     boolean print = true;
118     if (exampleValue == null) {
119       print = false;
120     } else if (exampleValue.isEmpty()) {
121       exampleValue = "(...)";
122     } else {
123       if (exampleValue.contains("$")) {
124         exampleValue = "'" + exampleValue + "'";
125       }
126       if (!exampleValue.endsWith(")")) {
127         exampleValue += " (for example)";
128       }
129     }
130 
131     String value = System.getProperty(property, defaultValue);
132 
133     if (print) {
134       final String overrideOrSet;
135       final String valueString;
136       if (value == null) {
137         valueString = "(not set)";
138         overrideOrSet = "set";
139       } else {
140         valueString = value;
141         overrideOrSet = "override";
142       }
143 
144       if (Objects.equals(defaultValue, exampleValue)) {
145         System.out.println(variable + "=" + valueString + " -- " + overrideOrSet + " with -D"
146             + property + "=" + "(...)");
147       } else {
148         System.out.println(variable + "=" + valueString + " -- " + overrideOrSet + " with -D"
149             + property + "=" + exampleValue);
150       }
151     }
152 
153     R returnValue;
154     if (valueConverter != null) {
155       returnValue = valueConverter.apply(value);
156     } else {
157       returnValue = (R) value;
158     }
159 
160     return returnValue;
161   }
162 
163   public static SocketAddress socketAddress(String socketName) throws IOException {
164     if (socketName.startsWith("file:")) {
165       // demo only: assume file: URLs are always handled by AFUNIXSocketAddress
166       return AFUNIXSocketAddress.of(URI.create(socketName));
167     } else if (socketName.contains(":/")) {
168       // assume URI, e.g., unix:// or tipc://
169       return AFSocketAddress.of(URI.create(socketName));
170     }
171 
172     int colon = socketName.lastIndexOf(':');
173     int slashOrBackslash = Math.max(socketName.lastIndexOf('/'), socketName.lastIndexOf('\\'));
174 
175     if (socketName.startsWith("@")) {
176       // abstract namespace (Linux only!)
177       return AFUNIXSocketAddress.inAbstractNamespace(socketName.substring(1));
178     } else if (colon > 0 && slashOrBackslash < colon && !socketName.startsWith("/")) {
179       // assume TCP socket
180       String hostname = socketName.substring(0, colon);
181       int port = Integer.parseInt(socketName.substring(colon + 1));
182       return new InetSocketAddress(hostname, port);
183     } else {
184       // assume unix socket file name
185       return AFUNIXSocketAddress.of(new File(socketName));
186     }
187   }
188 
189   public static Socket connectSocket(SocketAddress socketAddress) throws IOException {
190     if (socketAddress instanceof AFUNIXSocketAddress) {
191       return AFUNIXSocket.connectTo((AFUNIXSocketAddress) socketAddress);
192     } else {
193       Socket socket = new Socket();
194       socket.connect(socketAddress);
195       return socket;
196     }
197   }
198 
199   public static SocketAddress parseAddress(String[] args, SocketAddress defaultAddress)
200       throws IOException {
201     if (args.length == 0) {
202       return defaultAddress;
203     } else if (args.length == 1) {
204       return parseAddress("--unix", args[0], defaultAddress);
205     } else {
206       return parseAddress(args[0], args[1], defaultAddress);
207     }
208   }
209 
210   @SuppressFBWarnings("PATH_TRAVERSAL_IN")
211   public static SocketAddress parseAddress(String opt, String val, SocketAddress defaultAddress)
212       throws IOException {
213     if (opt == null || val == null) {
214       return defaultAddress;
215     }
216     switch (opt) {
217       case "--unix":
218         return AFUNIXSocketAddress.of(new File(val));
219       case "--url":
220         return AFSocketAddress.of(URI.create(val));
221       default:
222         throw new IllegalArgumentException("Valid parameters: --unix <path> OR --url <URL>");
223     }
224   }
225 }