All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.vectorprint.configuration.cdi.CDIProperties Maven / Gradle / Ivy

Go to download

This project is about configuration of applications and about parameterization of Objects. This library offers annotations (and annotation processors), parsers, typing, observing changes, serialization, cloning and more when working with settings and/or object parameters. Settings and its features can be declared using an xml format. The library offers syntax support for settings and parameters in a loosely coupled manner. You are not restricted to built in syntax, you can provide your own. At runtime this library tracks keys for which a default is used because they are not found in settings. Also it tracks unused keys. You can stack features for settings such as caching, preparing keys and values, readonlyness, threadsafety, helpsupport, reading / parsing from input. You can easily develop your own features for settings.

There is a newer version: 12.2
Show newest version
/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package com.vectorprint.configuration.cdi;

/*
 * #%L
 * VectorPrintConfig3.0
 * %%
 * Copyright (C) 2011 - 2013 VectorPrint
 * %%
 * Licensed 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.
 * #L%
 */

import com.vectorprint.configuration.EnhancedMap;
import com.vectorprint.configuration.binding.BindingHelper;
import com.vectorprint.configuration.binding.settings.SettingsBindingService;
import com.vectorprint.configuration.decoration.AbstractPropertiesDecorator;
import com.vectorprint.configuration.decoration.Changes;
import com.vectorprint.configuration.decoration.Observable;
import com.vectorprint.configuration.decoration.Observer;
import com.vectorprint.configuration.decoration.visiting.CacheClearingVisitor;
import com.vectorprint.configuration.decoration.visiting.ObservableVisitor;

import javax.enterprise.context.ApplicationScoped;
import javax.enterprise.context.spi.CreationalContext;
import javax.enterprise.inject.Produces;
import javax.enterprise.inject.spi.Annotated;
import javax.enterprise.inject.spi.AnnotatedCallable;
import javax.enterprise.inject.spi.AnnotatedField;
import javax.enterprise.inject.spi.AnnotatedMethod;
import javax.enterprise.inject.spi.AnnotatedParameter;
import javax.enterprise.inject.spi.Bean;
import javax.enterprise.inject.spi.BeanManager;
import javax.enterprise.inject.spi.CDI;
import javax.enterprise.inject.spi.InjectionPoint;
import javax.inject.Inject;
import java.awt.*;
import java.io.File;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;
import java.util.stream.Stream;

/**
 * A CDI Producer of properties allowing you to use @Inject in combination with
 * {@link Property}.
 * NOTE -parameters option for javac (java 8+) must be used when {@link Property} is used on
 * method parameters without {@link Property#keys()} and properties are reloadable.
 * @author Eduard Drenth at VectorPrint.nl
 * @see PropertyResolver
 */
@ApplicationScoped
public class CDIProperties extends AbstractPropertiesDecorator implements Observer {

    private static final BindingHelper bindingHelper = SettingsBindingService.getInstance().getFactory().getBindingHelper();

    @Inject
    public CDIProperties(@PropertyProducer EnhancedMap settings) {
        super(settings);
        ((AbstractPropertiesDecorator) settings).accept(new ObservableVisitor(this));
    }

    private final Map> injectionPoints =
            new HashMap>(100) {
                @Override
                public List get(Object key) {
                    if (!containsKey(key)) {
                        put((String) key, new ArrayList<>(3));
                    }
                    return super.get(key);
                }
            };

    private String[] getKeys(final InjectionPoint ip) {
        String[] rv = (ip.getAnnotated().getAnnotation(Property.class).keys().length > 0)
                ? ip.getAnnotated().getAnnotation(Property.class).keys()
                : new String[]{name(ip.getAnnotated())};
        Arrays.stream(rv).forEach(a -> injectionPoints.get(a).add(ip));
        return rv;
    }

    private static final Pattern ARGNAME = Pattern.compile("^arg[0-9]+");

    private String name(Annotated annotated) {
        String name = annotated instanceof AnnotatedField ?
                ((AnnotatedField)annotated).getJavaMember().getName() :
                ((AnnotatedParameter)annotated).getJavaParameter().getName();
        if (annotated instanceof AnnotatedParameter && ARGNAME.matcher(name).matches()) {
            throw new IllegalArgumentException(
                    String.format("compile using -parameters (java 8+) or use keys() argument to Property, to get" +
                            " real name for %s", ((AnnotatedParameter)annotated).getDeclaringCallable())
            );
        }
        return name;
    }

    private Object getDefault(final InjectionPoint ip) {
        Class clazz = (Class) ip.getAnnotated().getBaseType();
        if (ip.getAnnotated().getAnnotation(Property.class).defaultValue().length > 0) {
            return clazz.isArray()
                    ? bindingHelper.convert(ip.getAnnotated().getAnnotation(Property.class).defaultValue(), clazz)
                    : bindingHelper.convert(ip.getAnnotated().getAnnotation(Property.class).defaultValue()[0], clazz);
        } else if (boolean.class.equals(clazz) || Boolean.class.equals(clazz)) {
            return false;
        } else if (char.class.equals(clazz) || Character.class.equals(clazz)) {
            return '\u0000';
        } else if (double.class.equals(clazz) || Double.class.equals(clazz)) {
            return 0.0d;
        } else if (float.class.equals(clazz) || Float.class.equals(clazz)) {
            return 0.0f;
        } else if (int.class.equals(clazz) || Integer.class.equals(clazz)) {
            return 0;
        } else if (short.class.equals(clazz) || Short.class.equals(clazz)) {
            return 0;
        } else if (byte.class.equals(clazz) || Byte.class.equals(clazz)) {
            return 0;
        } else if (long.class.equals(clazz) || Long.class.equals(clazz)) {
            return 0;
        } else {
            return null;
        }
    }

    @Produces
    @Properties
    public EnhancedMap getEnhancedMap(InjectionPoint ip) {
        return this;
    }

    @Produces
    @Property
    @CheckRequired
    public File[] getFileProperties(InjectionPoint ip) {
        return getFileProperties((File[]) getDefault(ip), getKeys(ip));
    }

    @Produces
    @Property
    @CheckRequired
    public File getFileProperty(InjectionPoint ip) {
        return getFileProperty((File) getDefault(ip), getKeys(ip));
    }

    @Produces
    @Property
    @CheckRequired
    public Pattern[] getRegexProperties(InjectionPoint ip) {
        return getRegexProperties((Pattern[]) getDefault(ip), getKeys(ip));
    }

    @Produces
    @Property
    @CheckRequired
    public Pattern getRegexProperty(InjectionPoint ip) {
        return getRegexProperty((Pattern) getDefault(ip), getKeys(ip));
    }

    @Produces
    @Property
    @CheckRequired
    public Class[] getClassProperties(InjectionPoint ip) throws ClassNotFoundException {
        return getClassProperties((Class[]) getDefault(ip), getKeys(ip));
    }

    @Produces
    @Property
    @CheckRequired
    public Class getClassProperty(InjectionPoint ip) throws ClassNotFoundException {
        return getClassProperty((Class) getDefault(ip), getKeys(ip));
    }

    @Produces
    @Property
    @CheckRequired
    public Date[] getDateProperties(InjectionPoint ip) {
        return getDateProperties((Date[]) getDefault(ip), getKeys(ip));
    }

    @Produces
    @Property
    @CheckRequired
    public Date getDateProperty(InjectionPoint ip) {
        return getDateProperty((Date) getDefault(ip), getKeys(ip));
    }

    @Produces
    @Property
    @CheckRequired
    public byte[] getByteProperties(InjectionPoint ip) {
        return getByteProperties((byte[]) getDefault(ip), getKeys(ip));
    }

    @Produces
    @Property
    @CheckRequired
    public char[] getCharProperties(InjectionPoint ip) {
        return getCharProperties((char[]) getDefault(ip), getKeys(ip));
    }

    @Produces
    @Property
    @CheckRequired
    public short[] getShortProperties(InjectionPoint ip) {
        return getShortProperties((short[]) getDefault(ip), getKeys(ip));
    }

    @Produces
    @Property
    @CheckRequired
    public byte getByteProperty(InjectionPoint ip) {
        return getByteProperty((Byte) getDefault(ip), getKeys(ip));
    }

    @Produces
    @Property
    @CheckRequired
    public char getCharProperty(InjectionPoint ip) {
        return getCharProperty((Character) getDefault(ip), getKeys(ip));
    }

    @Produces
    @Property
    @CheckRequired
    public short getShortProperty(InjectionPoint ip) {
        return getShortProperty((Short) getDefault(ip), getKeys(ip));
    }

    @Produces
    @Property
    @CheckRequired
    public URL[] getURLProperties(InjectionPoint ip) throws MalformedURLException {
        return getURLProperties((URL[]) getDefault(ip), getKeys(ip));
    }

    @Produces
    @Property
    @CheckRequired
    public URL getURLProperty(InjectionPoint ip) throws MalformedURLException {
        return getURLProperty((URL) getDefault(ip), getKeys(ip));
    }

    @Produces
    @Property
    @CheckRequired
    public Color[] getColorProperties(InjectionPoint ip) {
        return getColorProperties((Color[]) getDefault(ip), getKeys(ip));
    }

    @Produces
    @Property
    @CheckRequired
    public boolean[] getBooleanProperties(InjectionPoint ip) {
        return getBooleanProperties((boolean[]) getDefault(ip), getKeys(ip));
    }

    @Produces
    @Property
    @CheckRequired
    public long[] getLongProperties(InjectionPoint ip) {
        return getLongProperties((long[]) getDefault(ip), getKeys(ip));
    }

    @Produces
    @Property
    @CheckRequired
    public int[] getIntegerProperties(InjectionPoint ip) {
        return getIntegerProperties((int[]) getDefault(ip), getKeys(ip));
    }

    @Produces
    @Property
    @CheckRequired
    public double[] getDoubleProperties(InjectionPoint ip) {
        return getDoubleProperties((double[]) getDefault(ip), getKeys(ip));
    }

    @Produces
    @Property
    @CheckRequired
    public float[] getFloatProperties(InjectionPoint ip) {
        return getFloatProperties((float[]) getDefault(ip), getKeys(ip));
    }

    @Produces
    @Property
    @CheckRequired
    public String[] getStringProperties(InjectionPoint ip) {
        return getStringProperties((String[]) getDefault(ip), getKeys(ip));
    }

    @Produces
    @Property
    @CheckRequired
    public String getProperty(InjectionPoint ip) {
        return getProperty((String) getDefault(ip), getKeys(ip));
    }

    @Produces
    @Property
    @CheckRequired
    public long getLongProperty(InjectionPoint ip) {
        return getLongProperty((Long) getDefault(ip), getKeys(ip));
    }

    @Produces
    @Property
    @CheckRequired
    public int getIntegerProperty(InjectionPoint ip) {
        return getIntegerProperty((Integer) getDefault(ip), getKeys(ip));
    }

    @Produces
    @Property
    @CheckRequired
    public float getFloatProperty(InjectionPoint ip) {
        return getFloatProperty((Float) getDefault(ip), getKeys(ip));
    }

    @Produces
    @Property
    @CheckRequired
    public double getDoubleProperty(InjectionPoint ip) {
        return getDoubleProperty((Double) getDefault(ip), getKeys(ip));
    }

    @Produces
    @Property
    @CheckRequired
    public Color getColorProperty(InjectionPoint ip) {
        return getColorProperty((Color) getDefault(ip), getKeys(ip));
    }

    @Produces
    @Property
    @CheckRequired
    public boolean getBooleanProperty(InjectionPoint ip) {
        return getBooleanProperty((Boolean) getDefault(ip), getKeys(ip));
    }

    @Override
    public EnhancedMap clone() throws CloneNotSupportedException {
        return super.clone();
    }

    @Override
    public void update(Observable object, Changes changes) {
        /*
        via the changes and injectionpoints we should be able to set
        new values
         */
        BeanManager beanManager = CDI.current().getBeanManager();

        List updates = new ArrayList<>();
        Stream.concat(
                changes.getChanged().stream(),
                changes.getAdded().stream()
        )
        .forEach(c -> injectionPoints.get(c).forEach(ip -> {
                Bean bean = ip.getBean();
                Class bc = bean.getBeanClass();
                CreationalContext creationalContext =
                        beanManager.createCreationalContext(bean);
                Object reference = beanManager.getReference(bean, bc, creationalContext);
                Annotated a = ip.getAnnotated();
                updates.add(new ToUpdate(a, reference, c));
            })
        );
        accept(CACHE_CLEARING_VISITOR);
        updates.forEach(u -> u.update(getGenericProperty(null, (Class) u.annotated.getBaseType(), u.key)));
    }

    private static final CacheClearingVisitor CACHE_CLEARING_VISITOR =
            new CacheClearingVisitor();

    private static class ToUpdate {
        private final Annotated annotated;
        private final Object reference;
        private final String key;

        public ToUpdate(Annotated annotated, Object reference, String key) {
            this.annotated = annotated;
            this.reference = reference;
            this.key = key;
        }

        void update(Object value) {
            if (annotated instanceof AnnotatedField) {
                Field f = ((AnnotatedField) annotated).getJavaMember();
                try {
                    boolean ac = f.isAccessible();
                    f.setAccessible(true);
                    f.set(reference, value);
                    f.setAccessible(ac);
                } catch (IllegalAccessException e) {
                    log.error(String.format("error updating %s with %s",
                            f, value));
                }
            } else {
                AnnotatedParameter ap = (AnnotatedParameter) annotated;
                Parameter javaParameter = ap.getJavaParameter();
                AnnotatedCallable declaringCallable = ap.getDeclaringCallable();
                if (declaringCallable instanceof AnnotatedMethod) {
                    Method method = ((AnnotatedMethod) declaringCallable).getJavaMember();
                    if (method.getParameterCount()==1) {
                        try {
                            method.invoke(reference,value);
                        } catch (IllegalAccessException | InvocationTargetException e) {
                            log.error(String.format("error calling %s with %s",
                                    method, value));
                        }
                    } else {
                        log.warn(String.format("%s has more than one argument, not supported yet",
                                method));
                    }
                } else {
                    log.warn(String.format("calling constructor %s not supported",declaringCallable));
                }
            }
        }
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy