| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| */ |
| package org.apache.tamaya.spisupport; |
| |
| import org.apache.tamaya.TypeLiteral; |
| import org.apache.tamaya.spi.*; |
| |
| import java.io.IOException; |
| import java.io.ObjectInputStream; |
| import java.io.ObjectOutputStream; |
| import java.io.Serializable; |
| import java.util.*; |
| import java.util.concurrent.locks.ReentrantReadWriteLock; |
| import java.util.logging.Logger; |
| import java.util.stream.Collectors; |
| |
| /** |
| * Default implementation of a simple {@link ConfigurationContext}. |
| */ |
| public class DefaultConfigurationContext implements ConfigurationContext, Serializable { |
| |
| /** The logger used. */ |
| private final static Logger LOG = Logger.getLogger(DefaultConfigurationContext.class.getName()); |
| |
| private MetadataProvider metaDataProvider; |
| |
| /** |
| * Subcomponent handling {@link PropertyConverter} instances. |
| */ |
| private PropertyConverterManager propertyConverterManager; |
| |
| /** |
| * The current unmodifiable createList of loaded {@link PropertySource} instances. |
| */ |
| private List<PropertySource> immutablePropertySources; |
| |
| /** |
| * The current unmodifiable createList of loaded {@link PropertyFilter} instances. |
| */ |
| private List<PropertyFilter> immutablePropertyFilters; |
| |
| /** The corresponding classLoader for this instance. */ |
| private ServiceContext serviceContext; |
| |
| |
| /** |
| * Lock for internal synchronization. |
| */ |
| private ReentrantReadWriteLock propertySourceLock = new ReentrantReadWriteLock(); |
| |
| @SuppressWarnings("unchecked") |
| protected DefaultConfigurationContext(DefaultConfigurationBuilder builder) { |
| this.serviceContext = builder.serviceContext; |
| this.metaDataProvider = Objects.requireNonNull(builder.metaDataProvider); |
| this.metaDataProvider.init(this); |
| propertyConverterManager = new PropertyConverterManager(serviceContext); |
| List<PropertySource> propertySources = new ArrayList<>(); |
| // first we load all PropertySources which got registered via java.util.ServiceLoader |
| propertySources.addAll(builder.propertySources); |
| // now sort them according to their ordinal values |
| immutablePropertySources = Collections.unmodifiableList(propertySources); |
| |
| // as next step we pick up the PropertyFilters pretty much the same way |
| List<PropertyFilter> propertyFilters = new ArrayList<>(builder.getPropertyFilters()); |
| immutablePropertyFilters = Collections.unmodifiableList(propertyFilters); |
| |
| // Finally addPropertyValue the converters |
| for(Map.Entry<TypeLiteral<?>, List<PropertyConverter<?>>> en:builder.getPropertyConverter().entrySet()) { |
| for (@SuppressWarnings("rawtypes") PropertyConverter converter : en.getValue()) { |
| this.propertyConverterManager.register(en.getKey(), converter); |
| } |
| } |
| LOG.info("Registered " + propertyConverterManager.getPropertyConverters().size() + " property converters: " + |
| propertyConverterManager.getPropertyConverters()); |
| } |
| |
| public DefaultConfigurationContext(ServiceContext serviceContext, |
| List<PropertyFilter> propertyFilters, List<PropertySource> propertySources, |
| Map<TypeLiteral<?>, List<PropertyConverter<?>>> propertyConverters, |
| MetadataProvider metaDataProvider) { |
| this.serviceContext = Objects.requireNonNull(serviceContext); |
| this.immutablePropertyFilters = Collections.unmodifiableList(new ArrayList<>(propertyFilters)); |
| this.immutablePropertySources = Collections.unmodifiableList(new ArrayList<>(propertySources)); |
| this.metaDataProvider = Objects.requireNonNull(metaDataProvider); |
| this.metaDataProvider.init(this); |
| propertyConverterManager = new PropertyConverterManager(serviceContext); |
| for(Map.Entry<TypeLiteral<?>, List<PropertyConverter<?>>> en:propertyConverters.entrySet()) { |
| for (@SuppressWarnings("rawtypes") PropertyConverter converter : en.getValue()) { |
| this.propertyConverterManager.register(en.getKey(), converter); |
| } |
| } |
| } |
| |
| |
| @Override |
| public Map<String,String> getMetaData(String key) { |
| return metaDataProvider.getMetaData(key); |
| } |
| |
| @Override |
| public ServiceContext getServiceContext() { |
| return serviceContext; |
| } |
| |
| @Override |
| public boolean equals(Object o) { |
| if (this == o) { |
| return true; |
| } |
| if (!(o instanceof DefaultConfigurationContext)){ |
| return false; |
| } |
| |
| DefaultConfigurationContext that = (DefaultConfigurationContext) o; |
| |
| if (!propertyConverterManager.equals(that.propertyConverterManager)) { |
| return false; |
| } |
| if (!immutablePropertySources.equals(that.immutablePropertySources)) { |
| return false; |
| } |
| if (!immutablePropertyFilters.equals(that.immutablePropertyFilters)) { |
| return false; |
| } |
| return true; |
| |
| } |
| |
| @Override |
| public int hashCode() { |
| int result = propertyConverterManager.hashCode(); |
| result = 31 * result + immutablePropertySources.hashCode(); |
| result = 31 * result + immutablePropertyFilters.hashCode(); |
| return result; |
| } |
| |
| @Override |
| public String toString() { |
| StringBuilder b = new StringBuilder("ConfigurationContext{\n"); |
| b.append(" Property Sources\n"); |
| b.append(" ----------------\n"); |
| if(immutablePropertySources.isEmpty()){ |
| b.append(" No property sources loaded.\n\n"); |
| }else { |
| b.append(" CLASS NAME ORDINAL SCANNABLE SIZE STATE ERROR\n\n"); |
| for (PropertySource ps : immutablePropertySources) { |
| b.append(" "); |
| appendFormatted(b, ps.getClass().getSimpleName(), 30); |
| appendFormatted(b, ps.getName(), 70); |
| appendFormatted(b, String.valueOf(PropertySourceComparator.getOrdinal(ps)), 8); |
| appendFormatted(b, String.valueOf(ps.isScannable()), 10); |
| if (ps.isScannable()) { |
| appendFormatted(b, String.valueOf(ps.getProperties().size()), 8); |
| } else { |
| appendFormatted(b, "-", 8); |
| } |
| PropertyValue state = ps.get("_state"); |
| if(state==null || state.getValue()==null){ |
| appendFormatted(b, "OK", 10); |
| }else { |
| appendFormatted(b, state.getValue(), 10); |
| if("ERROR".equals(state.getValue())){ |
| PropertyValue val = ps.get("_exception"); |
| if(val!=null) { |
| appendFormatted(b, val.getValue(), 30); |
| } |
| } |
| } |
| b.append('\n'); |
| } |
| b.append("\n"); |
| } |
| b.append(" Property Filters\n"); |
| b.append(" ----------------\n"); |
| if(immutablePropertyFilters.isEmpty()){ |
| b.append(" No property filters loaded.\n\n"); |
| }else { |
| b.append(" CLASS INFO\n\n"); |
| for (PropertyFilter filter : getPropertyFilters()) { |
| b.append(" "); |
| appendFormatted(b, filter.getClass().getSimpleName(), 30); |
| b.append(removeNewLines(filter.toString())); |
| b.append('\n'); |
| } |
| b.append("\n\n"); |
| } |
| b.append(" Property Converters\n"); |
| b.append(" -------------------\n"); |
| b.append(" CLASS TYPE INFO\n\n"); |
| for(Map.Entry<TypeLiteral<?>, List<PropertyConverter<?>>> converterEntry:getPropertyConverters().entrySet()){ |
| for(PropertyConverter converter: converterEntry.getValue()){ |
| b.append(" "); |
| appendFormatted(b, converter.getClass().getSimpleName(), 30); |
| appendFormatted(b, converterEntry.getKey().getRawType().getSimpleName(), 30); |
| b.append(removeNewLines(converter.toString())); |
| b.append('\n'); |
| } |
| } |
| b.append("\n}"); |
| return b.toString(); |
| } |
| |
| private void appendFormatted(StringBuilder b, String text, int length) { |
| int padding; |
| if(text==null){ |
| b.append("<null>"); |
| return; |
| } |
| if(text.length() <= (length)){ |
| b.append(text); |
| padding = length - text.length(); |
| }else{ |
| b.append(text.substring(0, length-1)); |
| padding = 1; |
| } |
| for(int i=0;i<padding;i++){ |
| b.append(' '); |
| } |
| } |
| |
| private String removeNewLines(String s) { |
| return s.replace('\n', ' ').replace('\r', ' '); |
| } |
| |
| |
| @Override |
| public List<PropertySource> getPropertySources() { |
| return immutablePropertySources; |
| } |
| |
| @Override |
| public PropertySource getPropertySource(String name) { |
| for(PropertySource ps:getPropertySources()){ |
| if(name.equals(ps.getName())){ |
| return ps; |
| } |
| } |
| return null; |
| } |
| |
| @Override |
| public Map<TypeLiteral<?>, List<PropertyConverter<?>>> getPropertyConverters() { |
| return propertyConverterManager.getPropertyConverters(); |
| } |
| |
| @Override |
| public <T> List<PropertyConverter<T>> getPropertyConverters(TypeLiteral<T> targetType) { |
| return propertyConverterManager.getPropertyConverters(targetType); |
| } |
| |
| @Override |
| public List<PropertyFilter> getPropertyFilters() { |
| return immutablePropertyFilters; |
| } |
| |
| /** |
| * Evaluates all present keys from the property sources loaded. |
| * @return the keys found, never null. |
| */ |
| private Set<String> getKeys() { |
| Set<String> keys = new HashSet<>(); |
| for(PropertySource ps:immutablePropertySources){ |
| keys.addAll(ps.getProperties().keySet()); |
| } |
| return keys; |
| } |
| |
| /** |
| * Deserialization only reads the property source snapshots from the stream. Converters, filters, |
| * meta data provider and the service context are reinitialized based on the current environment. |
| * @param ois the input stream |
| * @throws IOException if the stream is corrupted |
| * @throws ClassNotFoundException if s property source class cannot be serialized. |
| */ |
| private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException { |
| this.serviceContext = ServiceContextManager.getServiceContext(); |
| this.propertyConverterManager = new PropertyConverterManager( |
| this.serviceContext, true); |
| this.immutablePropertySources = Collections.unmodifiableList( |
| (List<PropertySource>)ois.readObject()); |
| this.immutablePropertyFilters = Collections.unmodifiableList( |
| this.serviceContext.getServices(PropertyFilter.class)); |
| this.metaDataProvider = this.serviceContext.getService(MetadataProvider.class); |
| propertySourceLock = new ReentrantReadWriteLock(); |
| } |
| |
| private void writeObject(ObjectOutputStream oos)throws IOException{ |
| // omit converters, they will be reloaded from scratch. |
| oos.writeObject(this.immutablePropertySources.stream() |
| .map(ps -> DefaultPropertySourceSnapshot.of(ps, getKeys())).collect(Collectors.toList())); |
| // omit filters, they will be reloaded from scratch |
| } |
| |
| } |