001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.juneau.commons.settings; 018 019import static org.apache.juneau.commons.utils.Shorts.*; 020 021import java.util.concurrent.atomic.*; 022import java.util.function.*; 023 024import org.apache.juneau.commons.runtime.*; 025 026/** 027 * Property source backed by {@link Args}. 028 */ 029public class ArgsPropertySource implements PropertySource { 030 031 private final Supplier<Args> argsSupplier; 032 033 /** 034 * Constructor. 035 * 036 * @param argsSupplier Supplier for args. 037 */ 038 public ArgsPropertySource(Supplier<Args> argsSupplier) { 039 this.argsSupplier = argsSupplier; 040 } 041 042 /** 043 * Creates a source using the default command-line discovery. 044 * 045 * @return A new source. 046 */ 047 public static ArgsPropertySource createDefault() { 048 return new ArgsPropertySource(ArgsPropertySource::getDefaultArgs); 049 } 050 051 /** 052 * Returns the default command-line args, discovered and parsed once per JVM. 053 * 054 * @return The cached default args. 055 */ 056 public static Args getDefaultArgs() { 057 return DefaultArgsHolder.INSTANCE; 058 } 059 060 /** 061 * Wraps a supplier so that it is invoked at most once, with the result reused on later calls. 062 * 063 * @param supplier The supplier to wrap. 064 * @return A supplier that caches the first result. 065 */ 066 static Supplier<Args> memoize(Supplier<Args> supplier) { 067 var cache = new AtomicReference<Args>(); 068 return () -> { 069 var v = cache.get(); 070 if (v == null) { 071 cache.compareAndSet(null, supplier.get()); 072 v = cache.get(); 073 } 074 return v; 075 }; 076 } 077 078 private static final class DefaultArgsHolder { 079 static final Args INSTANCE = createDefaultArgs(); 080 } 081 082 /** 083 * Discovers and parses the default command-line args without caching. 084 * 085 * @return A new args object. 086 */ 087 public static Args createDefaultArgs() { 088 var s = System.getProperty("sun.java.command"); 089 if (ine(s)) { 090 var i = s.indexOf(' '); 091 return new Args(i == -1 ? "" : s.substring(i + 1)); 092 } 093 return new Args(System.getProperty("juneau.args", "")); 094 } 095 096 @Override 097 public PropertyLookupResult get(String name) { 098 var args = argsSupplier.get(); 099 if (args == null) 100 return PropertyLookupResult.missing(); 101 if (isIndex(name)) { 102 try { 103 var v = args.get(Integer.parseInt(name)); 104 return v.isPresent() ? PropertyLookupResult.present(v) : PropertyLookupResult.missing(); 105 } catch (@SuppressWarnings("unused") NumberFormatException e) { 106 // Out of int range; fall through. 107 } 108 } 109 var values = args.getAll(name); 110 if (! values.isEmpty()) 111 return PropertyLookupResult.present(o(String.join(",", values))); 112 var v = args.get(name); 113 return v.isPresent() ? PropertyLookupResult.present(v) : PropertyLookupResult.missing(); 114 } 115 116 /** 117 * Returns <jk>true</jk> if the name has the shape of an integer (optional sign followed by digits). 118 * 119 * <p> 120 * Guards {@link Integer#parseInt(String)} so that ordinary non-numeric keys do not pay for a thrown exception. 121 */ 122 static boolean isIndex(String name) { 123 if (name == null) 124 return false; 125 var n = name.length(); 126 if (n == 0) 127 return false; 128 var i = 0; 129 var c = name.charAt(0); 130 if (c == '-' || c == '+') { 131 if (n == 1) 132 return false; 133 i = 1; 134 } 135 for (; i < n; i++) { 136 c = name.charAt(i); 137 if (c < '0' || c > '9') 138 return false; 139 } 140 return true; 141 } 142}