| 1 | package org.dcache.util; |
| 2 | |
| 3 | import java.util.Collections; |
| 4 | import java.util.Enumeration; |
| 5 | import java.util.Iterator; |
| 6 | import java.util.Properties; |
| 7 | import java.util.Set; |
| 8 | |
| 9 | import org.slf4j.Logger; |
| 10 | import org.slf4j.LoggerFactory; |
| 11 | |
| 12 | /** |
| 13 | * ReplaceableProperties that allows properties to be annotated as |
| 14 | * deprecated, obsolete or forbidden. A property is annotated as deprecated |
| 15 | * by prefixing the declaration with <code>(deprecated)</code>, as obsolete |
| 16 | * by prefixing the declaration with <code>(obsolete)</code>, and forbidden |
| 17 | * by prefixing it <code>(forbidden)</code>. |
| 18 | * |
| 19 | * These annotations have the following semantics: |
| 20 | * <ul> |
| 21 | * <li><i>deprecated</i> indicates that a property is supported but that a |
| 22 | * future version of dCache will likely remove that support. |
| 23 | * <li><i>obsolete</i> indicates that a property is no longer supported and |
| 24 | * that dCache will always behaves correctly without supporting this |
| 25 | * property. |
| 26 | * <li><i>forbidden</i> indicates that a property is no longer supported and |
| 27 | * dCache does not always behave correctly without further configuration or |
| 28 | * that support for some feature has been removed. |
| 29 | * </ul> |
| 30 | * <p> |
| 31 | * The intended behaviour of dCache when encountering sysadmin-supplied |
| 32 | * property assignment of some annotated property is dependent on the |
| 33 | * annotation. For deprecated and obsolete properties, a warning is emitted |
| 34 | * and dCache continues to start up. If the user assigns a value to a |
| 35 | * forbidden properties then dCache will refuse to start. |
| 36 | * <p> |
| 37 | * Such a declaration only affects following declarations. It does not affect |
| 38 | * any previous declarations of this property, nor does it generate any |
| 39 | * errors when such properties are referenced in any way. |
| 40 | */ |
| 41 | public class DeprecatableProperties extends ReplaceableProperties { |
| 42 | private static final long serialVersionUID = -5684848160314570455L; |
| 43 | |
| 44 | private final static Logger _log = |
| 45 | LoggerFactory.getLogger( DeprecatableProperties.class); |
| 46 | |
| 47 | private final static String FORBIDDEN = "(forbidden)"; |
| 48 | private final static String OBSOLETE = "(obsolete)"; |
| 49 | private final static String DEPRECATED = "(deprecated)"; |
| 50 | |
| 51 | public DeprecatableProperties(Properties properties) |
| 52 | { |
| 53 | super(properties); |
| 54 | } |
| 55 | |
| 56 | public boolean isForbidden(String key) |
| 57 | { |
| 58 | return getProperty(FORBIDDEN + key) != null; |
| 59 | } |
| 60 | |
| 61 | public boolean isObsolete(String key) |
| 62 | { |
| 63 | return getProperty(OBSOLETE + key) != null; |
| 64 | } |
| 65 | |
| 66 | public boolean isDeprecated(String key) |
| 67 | { |
| 68 | return getProperty(DEPRECATED + key) != null; |
| 69 | } |
| 70 | |
| 71 | @Override |
| 72 | public synchronized Object put(Object key, Object value) |
| 73 | { |
| 74 | String s = key.toString(); |
| 75 | actOnAnnotation(s); |
| 76 | if( isDeprecatedDeclaration(s)) { |
| 77 | super.put(s.substring(DEPRECATED.length()), value); |
| 78 | } |
| 79 | return super.put(key, value); |
| 80 | } |
| 81 | |
| 82 | private void actOnAnnotation(String key) |
| 83 | { |
| 84 | if( isForbidden( key)) { |
| 85 | String error = errorForForbiddenProperty( key); |
| 86 | throw new IllegalArgumentException( error); |
| 87 | } |
| 88 | |
| 89 | if( isObsolete( key)) { |
| 90 | _log.warn( "The property {} is no longer used.", key); |
| 91 | } else if( isDeprecated( key)) { |
| 92 | _log.warn( "The property {} is deprecated and will be removed.", key); |
| 93 | } |
| 94 | } |
| 95 | |
| 96 | private String errorForForbiddenProperty(String property) |
| 97 | { |
| 98 | String error = getProperty( FORBIDDEN + property); |
| 99 | |
| 100 | if( error.isEmpty()) { |
| 101 | error = "Adjusting property " + property + " is forbidden as different properties now control this aspect of dCache."; |
| 102 | } |
| 103 | |
| 104 | return error; |
| 105 | } |
| 106 | |
| 107 | @Override |
| 108 | public Enumeration<?> propertyNames() |
| 109 | { |
| 110 | return Collections.enumeration(stringPropertyNames()); |
| 111 | } |
| 112 | |
| 113 | @Override |
| 114 | public Set<String> stringPropertyNames() |
| 115 | { |
| 116 | Set<String> names = super.stringPropertyNames(); |
| 117 | Iterator<String> i = names.iterator(); |
| 118 | while (i.hasNext()) { |
| 119 | String name = i.next(); |
| 120 | if( isAnnotatedDeclaration(name)) { |
| 121 | i.remove(); |
| 122 | } |
| 123 | } |
| 124 | return names; |
| 125 | } |
| 126 | |
| 127 | private boolean isDeprecatedDeclaration(String name) |
| 128 | { |
| 129 | return name.startsWith(DEPRECATED); |
| 130 | } |
| 131 | |
| 132 | private boolean isAnnotatedDeclaration(String name) |
| 133 | { |
| 134 | boolean isDeprecated = isDeprecatedDeclaration( name); |
| 135 | |
| 136 | boolean isForbidden = name.startsWith(FORBIDDEN); |
| 137 | boolean isObsolete = name.startsWith(OBSOLETE); |
| 138 | |
| 139 | return isForbidden || isDeprecated || isObsolete; |
| 140 | } |
| 141 | } |