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.svl.vars; 018 019import org.apache.juneau.commons.settings.*; 020import org.apache.juneau.commons.svl.*; 021 022/** 023 * Unified property variable resolver. 024 * 025 * <p> 026 * The format for this var is <js>"$P{propertyName[,defaultValue]}"</js>. 027 * 028 * <h5 class='section'>Caller-scoped property sources:</h5> 029 * <p> 030 * Resolution consults the singleton {@link Settings} chain by default. Callers (notably the 031 * {@code @Value} injection path) that need to inject additional, caller-scoped 032 * {@link PropertySource}s in front of the global chain can attach them to the 033 * {@link VarResolverSession} as a session bean of type {@code PropertySource[]}: 034 * 035 * <p class='bjava'> 036 * <jv>session</jv>.bean(PropertySource[].<jk>class</jk>, <jk>new</jk> PropertySource[] { <jv>source1</jv>, <jv>source2</jv> }); 037 * </p> 038 * 039 * <p> 040 * When the session carries that bean, this var consults the array in order between the 041 * {@link Settings#getOverride(String) local/global override stores} (which still win — 042 * matching the existing test-override contract) and the regular {@link Settings#get(String) 043 * Settings sources chain}. Sources earlier in the array take precedence over sources later 044 * in the array. The contract is identical to today when no such session bean is attached. 045 */ 046public class PropertyVar extends DefaultingVar { 047 048 /** The name of this variable. */ 049 public static final String NAME = "P"; 050 051 /** 052 * Constructor. 053 */ 054 public PropertyVar() { 055 super(NAME); 056 } 057 058 @Override /* Overridden from Var */ 059 public String resolve(VarResolverSession session, String key) { 060 var scoped = session == null ? null : session.getBean(PropertySource[].class).orElse(null); 061 if (scoped == null || scoped.length == 0) 062 return Settings.get().get(key).orElse(null); 063 064 // Caller-scoped sources are present. Honor Settings local/global overrides first 065 // so test-override semantics still win over caller-scoped sources, then walk the 066 // caller-scoped chain, then fall through to the global sources list. 067 if (Settings.get().isOverridden(key)) 068 return Settings.get().getOverride(key).orElse(null); 069 for (var src : scoped) { 070 if (src == null) 071 continue; 072 var r = src.get(key); 073 if (r.isPresent()) 074 return r.value().orElse(null); 075 } 076 return Settings.get().get(key).orElse(null); 077 } 078}