http://git-wip-us.apache.org/repos/asf/zest-java/blob/1c722f44/core/api/src/main/java/org/apache/polygene/api/composite/DependencyDescriptor.java ---------------------------------------------------------------------- diff --git a/core/api/src/main/java/org/apache/polygene/api/composite/DependencyDescriptor.java b/core/api/src/main/java/org/apache/polygene/api/composite/DependencyDescriptor.java new file mode 100644 index 0000000..ec87e2c --- /dev/null +++ b/core/api/src/main/java/org/apache/polygene/api/composite/DependencyDescriptor.java @@ -0,0 +1,41 @@ +/* + * 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.polygene.api.composite; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Type; + +/** + * Composite dependency descriptor. + */ +public interface DependencyDescriptor +{ + Annotation injectionAnnotation(); + + Type injectionType(); + + Class<?> injectedClass(); + + Class<?> rawInjectionType(); + + boolean optional(); + + Annotation[] annotations(); +}
http://git-wip-us.apache.org/repos/asf/zest-java/blob/1c722f44/core/api/src/main/java/org/apache/polygene/api/composite/InjectedFieldDescriptor.java ---------------------------------------------------------------------- diff --git a/core/api/src/main/java/org/apache/polygene/api/composite/InjectedFieldDescriptor.java b/core/api/src/main/java/org/apache/polygene/api/composite/InjectedFieldDescriptor.java new file mode 100644 index 0000000..ce88a68 --- /dev/null +++ b/core/api/src/main/java/org/apache/polygene/api/composite/InjectedFieldDescriptor.java @@ -0,0 +1,32 @@ +/* + * 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.polygene.api.composite; + +import java.lang.reflect.Field; + +/** + * Composite injected field descriptor. + */ +public interface InjectedFieldDescriptor +{ + Field field(); + + DependencyDescriptor dependency(); +} http://git-wip-us.apache.org/repos/asf/zest-java/blob/1c722f44/core/api/src/main/java/org/apache/polygene/api/composite/InjectedMethodDescriptor.java ---------------------------------------------------------------------- diff --git a/core/api/src/main/java/org/apache/polygene/api/composite/InjectedMethodDescriptor.java b/core/api/src/main/java/org/apache/polygene/api/composite/InjectedMethodDescriptor.java new file mode 100644 index 0000000..0ad4f70 --- /dev/null +++ b/core/api/src/main/java/org/apache/polygene/api/composite/InjectedMethodDescriptor.java @@ -0,0 +1,31 @@ +/* + * 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.polygene.api.composite; + +import java.lang.reflect.Method; + +/** + * Composite injected method descriptor. + */ +public interface InjectedMethodDescriptor +{ + Method method(); +} http://git-wip-us.apache.org/repos/asf/zest-java/blob/1c722f44/core/api/src/main/java/org/apache/polygene/api/composite/InjectedParametersDescriptor.java ---------------------------------------------------------------------- diff --git a/core/api/src/main/java/org/apache/polygene/api/composite/InjectedParametersDescriptor.java b/core/api/src/main/java/org/apache/polygene/api/composite/InjectedParametersDescriptor.java new file mode 100644 index 0000000..5361575 --- /dev/null +++ b/core/api/src/main/java/org/apache/polygene/api/composite/InjectedParametersDescriptor.java @@ -0,0 +1,28 @@ +/* + * 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.polygene.api.composite; + +/** + * Composite constructors and method injected parameters descriptor. + */ +public interface InjectedParametersDescriptor +{ +} http://git-wip-us.apache.org/repos/asf/zest-java/blob/1c722f44/core/api/src/main/java/org/apache/polygene/api/composite/InvalidCompositeException.java ---------------------------------------------------------------------- diff --git a/core/api/src/main/java/org/apache/polygene/api/composite/InvalidCompositeException.java b/core/api/src/main/java/org/apache/polygene/api/composite/InvalidCompositeException.java new file mode 100644 index 0000000..d9de0c7 --- /dev/null +++ b/core/api/src/main/java/org/apache/polygene/api/composite/InvalidCompositeException.java @@ -0,0 +1,32 @@ +/* + * 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.polygene.api.composite; + +/** + * This exception is thrown if a Composite is invalid. + */ +public class InvalidCompositeException + extends RuntimeException +{ + public InvalidCompositeException( String message ) + { + super( message ); + } +} http://git-wip-us.apache.org/repos/asf/zest-java/blob/1c722f44/core/api/src/main/java/org/apache/polygene/api/composite/InvalidValueCompositeException.java ---------------------------------------------------------------------- diff --git a/core/api/src/main/java/org/apache/polygene/api/composite/InvalidValueCompositeException.java b/core/api/src/main/java/org/apache/polygene/api/composite/InvalidValueCompositeException.java new file mode 100644 index 0000000..c857a6e --- /dev/null +++ b/core/api/src/main/java/org/apache/polygene/api/composite/InvalidValueCompositeException.java @@ -0,0 +1,32 @@ +/* + * 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.polygene.api.composite; + +/** + * This exception is thrown if a ValueComposite is invalid. + */ +public class InvalidValueCompositeException + extends RuntimeException +{ + public InvalidValueCompositeException( String message ) + { + super( message ); + } +} http://git-wip-us.apache.org/repos/asf/zest-java/blob/1c722f44/core/api/src/main/java/org/apache/polygene/api/composite/MethodDescriptor.java ---------------------------------------------------------------------- diff --git a/core/api/src/main/java/org/apache/polygene/api/composite/MethodDescriptor.java b/core/api/src/main/java/org/apache/polygene/api/composite/MethodDescriptor.java new file mode 100644 index 0000000..f0a991d --- /dev/null +++ b/core/api/src/main/java/org/apache/polygene/api/composite/MethodDescriptor.java @@ -0,0 +1,31 @@ +/* + * 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.polygene.api.composite; + +import java.lang.reflect.Method; + +/** + * Composite Method Descriptor. + */ +public interface MethodDescriptor +{ + Method method(); +} http://git-wip-us.apache.org/repos/asf/zest-java/blob/1c722f44/core/api/src/main/java/org/apache/polygene/api/composite/MissingMethodException.java ---------------------------------------------------------------------- diff --git a/core/api/src/main/java/org/apache/polygene/api/composite/MissingMethodException.java b/core/api/src/main/java/org/apache/polygene/api/composite/MissingMethodException.java new file mode 100644 index 0000000..a6e5998 --- /dev/null +++ b/core/api/src/main/java/org/apache/polygene/api/composite/MissingMethodException.java @@ -0,0 +1,38 @@ +/* + * 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.polygene.api.composite; + +/** + * This exception is thrown if client code tries to invoke a non-existing Composite method. + */ +public class MissingMethodException + extends RuntimeException +{ + public MissingMethodException( String message ) + { + super( message ); + } + + public MissingMethodException( String message, NoSuchMethodException e ) + { + super(message,e); + } +} http://git-wip-us.apache.org/repos/asf/zest-java/blob/1c722f44/core/api/src/main/java/org/apache/polygene/api/composite/ModelDescriptor.java ---------------------------------------------------------------------- diff --git a/core/api/src/main/java/org/apache/polygene/api/composite/ModelDescriptor.java b/core/api/src/main/java/org/apache/polygene/api/composite/ModelDescriptor.java new file mode 100644 index 0000000..cd7ddcf --- /dev/null +++ b/core/api/src/main/java/org/apache/polygene/api/composite/ModelDescriptor.java @@ -0,0 +1,41 @@ +/* + * 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.polygene.api.composite; + +import org.apache.polygene.api.common.Visibility; +import org.apache.polygene.api.structure.MetaInfoHolder; +import org.apache.polygene.api.structure.ModuleDescriptor; +import org.apache.polygene.api.type.HasTypes; + +/** + * Composite ModelDescriptor. + */ +public interface ModelDescriptor extends HasTypes, MetaInfoHolder +{ + Visibility visibility(); + + /** The Module that the Model is declared in. + * + * @return The Module that this Model was declared in. + */ + ModuleDescriptor module(); + + boolean isAssignableTo( Class<?> type ); +} http://git-wip-us.apache.org/repos/asf/zest-java/blob/1c722f44/core/api/src/main/java/org/apache/polygene/api/composite/NoSuchCompositeException.java ---------------------------------------------------------------------- diff --git a/core/api/src/main/java/org/apache/polygene/api/composite/NoSuchCompositeException.java b/core/api/src/main/java/org/apache/polygene/api/composite/NoSuchCompositeException.java new file mode 100644 index 0000000..e03eafc --- /dev/null +++ b/core/api/src/main/java/org/apache/polygene/api/composite/NoSuchCompositeException.java @@ -0,0 +1,59 @@ +/* + * 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.polygene.api.composite; + +import org.apache.polygene.api.common.InvalidApplicationException; + +/** + * This exception is thrown if client code tries to create a non-existing Composite type. + */ +public class NoSuchCompositeException + extends InvalidApplicationException +{ + private static final long serialVersionUID = 1L; + + private final String compositeType; + private final String moduleName; + private final String visibleTypes; + + protected NoSuchCompositeException( String metaType, String compositeType, String moduleName, String visibleTypes ) + { + super( "Could not find any visible " + metaType + " of type [" + compositeType + "] in module [" + + moduleName + "].\n" + visibleTypes ); + this.compositeType = compositeType; + this.moduleName = moduleName; + this.visibleTypes = visibleTypes; + } + + public String compositeType() + { + return compositeType; + } + + public String moduleName() + { + return moduleName; + } + + public String visibleTypes() + { + return visibleTypes; + } +} http://git-wip-us.apache.org/repos/asf/zest-java/blob/1c722f44/core/api/src/main/java/org/apache/polygene/api/composite/NoSuchTransientException.java ---------------------------------------------------------------------- diff --git a/core/api/src/main/java/org/apache/polygene/api/composite/NoSuchTransientException.java b/core/api/src/main/java/org/apache/polygene/api/composite/NoSuchTransientException.java new file mode 100644 index 0000000..6f6bfd7 --- /dev/null +++ b/core/api/src/main/java/org/apache/polygene/api/composite/NoSuchTransientException.java @@ -0,0 +1,42 @@ +/* + * 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.polygene.api.composite; + +import java.util.stream.Collectors; +import org.apache.polygene.api.structure.TypeLookup; + +/** + * This exception is thrown if client code tries to create a non-existing TransientComposite type. + */ +public class NoSuchTransientException extends NoSuchCompositeException +{ + public NoSuchTransientException( String typeName, String moduleName, TypeLookup typeLookup ) + { + super( "TransientComposite", typeName, moduleName, formatVisibleTypes( typeLookup ) ); + } + + private static String formatVisibleTypes( TypeLookup typeLookup ) + { + return typeLookup.allTransients() + .map(descriptor -> descriptor.primaryType().getName()) + .collect( Collectors.joining( "\n", "Visible transient types are:\n", "" ) ); + } +} http://git-wip-us.apache.org/repos/asf/zest-java/blob/1c722f44/core/api/src/main/java/org/apache/polygene/api/composite/PropertyMapper.java ---------------------------------------------------------------------- diff --git a/core/api/src/main/java/org/apache/polygene/api/composite/PropertyMapper.java b/core/api/src/main/java/org/apache/polygene/api/composite/PropertyMapper.java new file mode 100644 index 0000000..0591bd4 --- /dev/null +++ b/core/api/src/main/java/org/apache/polygene/api/composite/PropertyMapper.java @@ -0,0 +1,655 @@ +/* + * 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.polygene.api.composite; + +import java.io.IOException; +import java.io.InputStream; +import java.lang.reflect.Array; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.math.BigDecimal; +import java.math.BigInteger; +import java.time.Duration; +import java.time.Instant; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.OffsetDateTime; +import java.time.Period; +import java.time.ZonedDateTime; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Properties; +import java.util.Set; +import org.apache.polygene.api.PolygeneAPI; +import org.apache.polygene.api.property.GenericPropertyInfo; +import org.apache.polygene.api.property.Property; +import org.apache.polygene.api.util.Classes; +import org.apache.polygene.api.value.ValueComposite; + +/** + * Transfer java.util.Properties to Composite properties + */ +public final class PropertyMapper +{ + + private final static Map<Type, MappingStrategy> STRATEGY; + + static + { + STRATEGY = new HashMap<>(); + STRATEGY.put( Integer.class, new IntegerMapper() ); + STRATEGY.put( Long.class, new LongMapper() ); + STRATEGY.put( Short.class, new ShortMapper() ); + STRATEGY.put( Byte.class, new ByteMapper() ); + STRATEGY.put( String.class, new StringMapper() ); + STRATEGY.put( Character.class, new CharMapper() ); + STRATEGY.put( Float.class, new FloatMapper() ); + STRATEGY.put( Double.class, new DoubleMapper() ); + STRATEGY.put( LocalDate.class, new LocalDateMapper() ); + STRATEGY.put( LocalDateTime.class, new LocalDateTimeMapper() ); + STRATEGY.put( ZonedDateTime.class, new ZonedDateTimeMapper() ); + STRATEGY.put( OffsetDateTime.class, new OffsetDateTimeMapper() ); + STRATEGY.put( Instant.class, new InstantMapper() ); + STRATEGY.put( Duration.class, new DurationMapper() ); + STRATEGY.put( Period.class, new PeriodMapper() ); + STRATEGY.put( Boolean.class, new BooleanMapper() ); + STRATEGY.put( BigDecimal.class, new BigDecimalMapper() ); + STRATEGY.put( BigInteger.class, new BigIntegerMapper() ); + STRATEGY.put( Enum.class, new EnumMapper() ); + STRATEGY.put( Array.class, new ArrayMapper() ); + STRATEGY.put( Map.class, new MapMapper() ); + STRATEGY.put( List.class, new ListMapper() ); + STRATEGY.put( Set.class, new SetMapper() ); + STRATEGY.put( ValueComposite.class, new ValueCompositeMapper() ); + } + + /** + * Populate the Composite with properties from the given properties object. + * + * @param props properties object + * @param composite the composite instance + * + * @throws IllegalArgumentException if properties could not be transferred to composite + */ + public static void map( Properties props, Composite composite ) + throws IllegalArgumentException + { + for( Map.Entry<Object, Object> objectObjectEntry : props.entrySet() ) + { + try + { + String methodName = objectObjectEntry.getKey().toString(); + Method propertyMethod = composite.getClass().getInterfaces()[ 0 ].getMethod( methodName ); + propertyMethod.setAccessible( true ); + Object value = objectObjectEntry.getValue(); + Type propertyType = GenericPropertyInfo.propertyTypeOf( propertyMethod ); + + value = mapToType( composite, propertyType, value.toString() ); + + @SuppressWarnings( "unchecked" ) + Property<Object> property = (Property<Object>) propertyMethod.invoke( composite ); + property.set( value ); + } + catch( NoSuchMethodException e ) + { + throw new IllegalArgumentException( "Could not find any property named " + objectObjectEntry.getKey() ); + } + catch( IllegalAccessException e ) + { + //noinspection ThrowableInstanceNeverThrown + throw new IllegalArgumentException( "Could not populate property named " + objectObjectEntry.getKey(), e ); + } + catch( InvocationTargetException e ) + { + //noinspection ThrowableInstanceNeverThrown + String message = "Could not populate property named " + objectObjectEntry.getKey(); + throw new IllegalArgumentException( message, e ); + } + } + } + + @SuppressWarnings( "raw" ) + private static Object mapToType( Composite composite, Type propertyType, Object value ) + { + final String stringValue = value.toString(); + MappingStrategy strategy; + if( propertyType instanceof Class ) + { + Class type = (Class) propertyType; + if( type.isArray() ) + { + strategy = STRATEGY.get( Array.class ); + } + else if( Enum.class.isAssignableFrom( Classes.RAW_CLASS.apply( propertyType ) ) ) + { + strategy = STRATEGY.get( Enum.class ); + } + else + { + strategy = STRATEGY.get( type ); + } + if( strategy == null ) // If null, try with the ValueComposite Mapper... + { + strategy = STRATEGY.get( ValueComposite.class ); + } + } + else if( propertyType instanceof ParameterizedType ) + { + ParameterizedType type = ( (ParameterizedType) propertyType ); + + if( type.getRawType() instanceof Class ) + { + Class clazz = (Class) type.getRawType(); + if( List.class.isAssignableFrom( clazz ) ) + { + strategy = STRATEGY.get( List.class ); + } + else if( Set.class.isAssignableFrom( clazz ) ) + { + strategy = STRATEGY.get( Set.class ); + } + else if( Map.class.isAssignableFrom( clazz ) ) + { + strategy = STRATEGY.get( Map.class ); + } + else + { + throw new IllegalArgumentException( propertyType + " is not supported." ); + } + } + else + { + throw new IllegalArgumentException( propertyType + " is not supported." ); + } + } + else + { + throw new IllegalArgumentException( propertyType + " is not supported." ); + } + + if( strategy == null ) + { + throw new IllegalArgumentException( propertyType + " is not supported." ); + } + + return strategy.map( composite, propertyType, stringValue ); + } + + /** + * Load a Properties object from the given stream, close it, and then populate + * the Composite with the properties. + * + * @param propertyInputStream properties input stream + * @param composite the instance + * + * @throws IOException if the stream could not be read + */ + + public static void map( InputStream propertyInputStream, Composite composite ) + throws IOException + { + if( propertyInputStream != null ) + { + Properties configProps = new Properties(); + try + { + configProps.load( propertyInputStream ); + } + finally + { + propertyInputStream.close(); + } + map( configProps, composite ); + } + } + + /** + * Create Properties object which is backed by the given Composite. + * + * @param composite the instance + * + * @return properties instance + */ + public static Properties toJavaProperties( final Composite composite ) + { + return new Properties() + { + private static final long serialVersionUID = 3550125427530538865L; + + @Override + public Object get( Object o ) + { + try + { + Method propertyMethod = composite.getClass().getMethod( o.toString() ); + Property<?> property = (Property<?>) propertyMethod.invoke( composite ); + return property.get(); + } + catch( NoSuchMethodException | IllegalAccessException | InvocationTargetException e ) + { + return null; + } + } + + @Override + public Object put( Object o, Object o1 ) + { + Object oldValue = get( o ); + + try + { + Method propertyMethod = composite.getClass().getMethod( o.toString(), Object.class ); + propertyMethod.invoke( composite, o1 ); + } + catch( NoSuchMethodException | IllegalAccessException | InvocationTargetException e ) + { + e.printStackTrace(); + } + + return oldValue; + } + }; + } + + private static void tokenize( String valueString, boolean mapSyntax, TokenizerCallback callback ) + { + char[] data = valueString.toCharArray(); + + int oldPos = 0; + for( int pos = 0; pos < data.length; pos++ ) + { + char ch = data[ pos ]; + if( ch == '\"' ) + { + pos = resolveQuotes( valueString, callback, data, pos, '\"' ); + oldPos = pos; + } + if( ch == '\'' ) + { + pos = resolveQuotes( valueString, callback, data, pos, '\'' ); + oldPos = pos; + } + if( ch == ',' || ( mapSyntax && ch == ':' ) ) + { + String token = new String( data, oldPos, pos - oldPos ); + callback.token( token ); + oldPos = pos + 1; + } + } + String token = new String( data, oldPos, data.length - oldPos ); + callback.token( token ); + } + + private static int resolveQuotes( String valueString, + TokenizerCallback callback, + char[] data, + int pos, char quote + ) + { + boolean found = false; + for( int j = pos + 1; j < data.length; j++ ) + { + if( !found ) + { + if( data[ j ] == quote ) + { + String token = new String( data, pos + 1, j - pos - 1 ); + callback.token( token ); + found = true; + } + } + else + { + if( data[ j ] == ',' ) + { + return j + 1; + } + } + } + if( !found ) + { + throw new IllegalArgumentException( "String is not quoted correctly: " + valueString ); + } + return data.length; + } + + private interface TokenizerCallback + { + void token( String token ); + } + + private interface MappingStrategy + { + Object map( Composite composite, Type type, String value ); + } + + private static class StringMapper + implements MappingStrategy + { + @Override + public Object map( Composite composite, Type type, String value ) + { + return value; + } + } + + private static class IntegerMapper + implements MappingStrategy + { + @Override + public Object map( Composite composite, Type type, String value ) + { + return new Integer( value.trim() ); + } + } + + private static class FloatMapper + implements MappingStrategy + { + @Override + public Object map( Composite composite, Type type, String value ) + { + return new Float( value.trim() ); + } + } + + private static class DoubleMapper + implements MappingStrategy + { + @Override + public Object map( Composite composite, Type type, String value ) + { + return new Double( value.trim() ); + } + } + + private static class LongMapper + implements MappingStrategy + { + @Override + public Object map( Composite composite, Type type, String value ) + { + return new Long( value.trim() ); + } + } + + private static class ShortMapper + implements MappingStrategy + { + @Override + public Object map( Composite composite, Type type, String value ) + { + return new Short( value.trim() ); + } + } + + private static class ByteMapper + implements MappingStrategy + { + @Override + public Object map( Composite composite, Type type, String value ) + { + return new Byte( value.trim() ); + } + } + + private static class CharMapper + implements MappingStrategy + { + @Override + public Object map( Composite composite, Type type, String value ) + { + return value.trim().charAt( 0 ); + } + } + + private static class BigDecimalMapper + implements MappingStrategy + { + @Override + public Object map( Composite composite, Type type, String value ) + { + return new BigDecimal( value.trim() ); + } + } + + private static class BigIntegerMapper + implements MappingStrategy + { + @Override + public Object map( Composite composite, Type type, String value ) + { + return new BigInteger( value.trim() ); + } + } + + private static class EnumMapper + implements MappingStrategy + { + @Override + @SuppressWarnings( "unchecked" ) + public Object map( Composite composite, Type type, String value ) + { + return Enum.valueOf( (Class<Enum>) type, value ); + } + } + + private static class LocalDateMapper + implements MappingStrategy + { + @Override + public Object map( Composite composite, Type type, String value ) + { + return LocalDate.parse( value.trim() ); + } + } + + private static class LocalDateTimeMapper + implements MappingStrategy + { + @Override + public Object map( Composite composite, Type type, String value ) + { + return LocalDateTime.parse( value.trim() ); + } + } + + private static class ZonedDateTimeMapper + implements MappingStrategy + { + @Override + public Object map( Composite composite, Type type, String value ) + { + return ZonedDateTime.parse( value.trim() ); + } + } + + private static class OffsetDateTimeMapper + implements MappingStrategy + { + @Override + public Object map( Composite composite, Type type, String value ) + { + return OffsetDateTime.parse( value.trim() ); + } + } + + private static class InstantMapper + implements MappingStrategy + { + @Override + public Object map( Composite composite, Type type, String value ) + { + return Instant.parse( value.trim() ); + } + } + + private static class DurationMapper + implements MappingStrategy + { + @Override + public Object map( Composite composite, Type type, String value ) + { + return Duration.parse( value.trim() ); + } + } + + private static class PeriodMapper + implements MappingStrategy + { + @Override + public Object map( Composite composite, Type type, String value ) + { + return Period.parse( value.trim() ); + } + } + + private static class ValueCompositeMapper + implements MappingStrategy + { + @Override + @SuppressWarnings( "unchecked" ) + public Object map( Composite composite, Type type, String value ) + { + return PolygeneAPI.FUNCTION_COMPOSITE_INSTANCE_OF.apply( composite ) + .module() + .instance() + .newValueFromSerializedState( (Class<Object>) type, value ); + } + } + + private static class ArrayMapper + implements MappingStrategy + { + @Override + @SuppressWarnings( { "raw", "unchecked" } ) + public Object map( final Composite composite, Type type, String value ) + { + final Class arrayType = ( (Class) type ).getComponentType(); + final ArrayList result = new ArrayList(); + tokenize( value, false, new TokenizerCallback() + { + @Override + public void token( String token ) + { + result.add( mapToType( composite, arrayType, token ) ); + } + } ); + return result.toArray( (Object[]) Array.newInstance( arrayType, result.size() ) ); + } + } + + private static class BooleanMapper + implements MappingStrategy + { + @Override + public Object map( final Composite composite, Type type, String value ) + { + return Boolean.valueOf( value.trim() ); + } + } + + private static class ListMapper + implements MappingStrategy + { + @Override + @SuppressWarnings( { "raw", "unchecked" } ) + public Object map( final Composite composite, Type type, String value ) + { + final Type dataType = ( (ParameterizedType) type ).getActualTypeArguments()[ 0 ]; + final Collection result = new ArrayList(); + tokenize( value, false, new TokenizerCallback() + { + @Override + public void token( String token ) + { + result.add( mapToType( composite, dataType, token ) ); + } + } ); + return result; + } + } + + private static class SetMapper + implements MappingStrategy + { + @Override + @SuppressWarnings( { "raw", "unchecked" } ) + public Object map( final Composite composite, Type type, String value ) + { + final Type dataType = ( (ParameterizedType) type ).getActualTypeArguments()[ 0 ]; + final Collection result = new HashSet(); + tokenize( value, false, new TokenizerCallback() + { + @Override + public void token( String token ) + { + result.add( mapToType( composite, dataType, token ) ); + } + } ); + return result; + } + } + + private static class MapMapper + implements MappingStrategy + { + @Override + @SuppressWarnings( { "raw", "unchecked" } ) + public Object map( final Composite composite, Type generictype, String value ) + { + ParameterizedType type = (ParameterizedType) generictype; + final Type keyType = type.getActualTypeArguments()[ 0 ]; + final Type valueType = type.getActualTypeArguments()[ 0 ]; + final Map result = new HashMap(); + tokenize( value, true, new TokenizerCallback() + { + boolean keyArrivingNext = true; + String key; + + @Override + public void token( String token ) + { + if( keyArrivingNext ) + { + key = token; + keyArrivingNext = false; + } + else + { + result.put( mapToType( composite, keyType, key ), mapToType( composite, valueType, token ) ); + keyArrivingNext = true; + } + } + } ); + return result; + } + } + + private PropertyMapper() + { + } +} http://git-wip-us.apache.org/repos/asf/zest-java/blob/1c722f44/core/api/src/main/java/org/apache/polygene/api/composite/StateDescriptor.java ---------------------------------------------------------------------- diff --git a/core/api/src/main/java/org/apache/polygene/api/composite/StateDescriptor.java b/core/api/src/main/java/org/apache/polygene/api/composite/StateDescriptor.java new file mode 100644 index 0000000..20c07c2 --- /dev/null +++ b/core/api/src/main/java/org/apache/polygene/api/composite/StateDescriptor.java @@ -0,0 +1,39 @@ +/* + * 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.polygene.api.composite; + +import java.util.stream.Stream; +import org.apache.polygene.api.common.QualifiedName; +import org.apache.polygene.api.property.PropertyDescriptor; + +/** + * Composite State Descriptor. + */ +public interface StateDescriptor +{ + PropertyDescriptor findPropertyModelByName( String name ) + throws IllegalArgumentException; + + PropertyDescriptor findPropertyModelByQualifiedName( QualifiedName name ) + throws IllegalArgumentException; + + Stream<? extends PropertyDescriptor> properties(); +} http://git-wip-us.apache.org/repos/asf/zest-java/blob/1c722f44/core/api/src/main/java/org/apache/polygene/api/composite/StatefulCompositeDescriptor.java ---------------------------------------------------------------------- diff --git a/core/api/src/main/java/org/apache/polygene/api/composite/StatefulCompositeDescriptor.java b/core/api/src/main/java/org/apache/polygene/api/composite/StatefulCompositeDescriptor.java new file mode 100644 index 0000000..42cdcb3 --- /dev/null +++ b/core/api/src/main/java/org/apache/polygene/api/composite/StatefulCompositeDescriptor.java @@ -0,0 +1,28 @@ +/* + * 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.polygene.api.composite; + +/** + * Stateful Composite Descriptor. + */ +public interface StatefulCompositeDescriptor +{ + StateDescriptor state(); +} http://git-wip-us.apache.org/repos/asf/zest-java/blob/1c722f44/core/api/src/main/java/org/apache/polygene/api/composite/TransientBuilder.java ---------------------------------------------------------------------- diff --git a/core/api/src/main/java/org/apache/polygene/api/composite/TransientBuilder.java b/core/api/src/main/java/org/apache/polygene/api/composite/TransientBuilder.java new file mode 100644 index 0000000..e42a904 --- /dev/null +++ b/core/api/src/main/java/org/apache/polygene/api/composite/TransientBuilder.java @@ -0,0 +1,71 @@ +/* + * 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.polygene.api.composite; + +import org.apache.polygene.api.common.ConstructionException; + +/** + * TransientBuilders are used to instantiate TransientComposites. They can be acquired from + * {@link TransientBuilderFactory#newTransientBuilder(Class)} and allows the client + * to provide additional settings before instantiating the TransientComposite. + */ +public interface TransientBuilder<T> +{ + /** + * Provide objects that can be injected into mixins that has the @Uses + * dependency injection annotation. + * + * @param usedObjects The objects that can be injected into mixins. + * + * @return the transient builder instance + * + * @see org.apache.polygene.api.injection.scope.Uses + */ + TransientBuilder<T> use( Object... usedObjects ); + + /** + * Get a representation of the state for the new Composite. + * It is possible to access and update properties and associations, + * even immutable ones since the builder represents the initial state. + * + * @return a proxy implementing the Composite type + */ + T prototype(); + + /** + * Get a representation of the state of the given type for the new Composite. + * This is primarily used if you want to provide state for a private mixin type. + * + * @param mixinType the mixin which you want to provide state for + * + * @return a proxy implementing the given mixin type + */ + <K> K prototypeFor( Class<K> mixinType ); + + /** + * Create a new Composite instance. + * + * @return a new Composite instance + * + * @throws ConstructionException thrown if it was not possible to instantiate the Composite + */ + T newInstance() + throws ConstructionException; +} http://git-wip-us.apache.org/repos/asf/zest-java/blob/1c722f44/core/api/src/main/java/org/apache/polygene/api/composite/TransientBuilderFactory.java ---------------------------------------------------------------------- diff --git a/core/api/src/main/java/org/apache/polygene/api/composite/TransientBuilderFactory.java b/core/api/src/main/java/org/apache/polygene/api/composite/TransientBuilderFactory.java new file mode 100644 index 0000000..7c6c7a1 --- /dev/null +++ b/core/api/src/main/java/org/apache/polygene/api/composite/TransientBuilderFactory.java @@ -0,0 +1,59 @@ +/* + * 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.polygene.api.composite; + +import org.apache.polygene.api.common.ConstructionException; + +/** + * This factory creates TransientComposites and the TransientBuilders. + * + * TransientComposite instances are very flexible in what it can reference, but are restricted in where they + * can be used. So, TransientComposites are mainly recommended where Values, Entities and Services can not be used, + * but they can also not be used to store state, be serialized across a network or have automatic equals/hashCode + * calculations. + */ +public interface TransientBuilderFactory +{ + /** + * Create a builder for creating new TransientComposites that implements the given TransientComposite type. + * + * @param mixinType an interface that describes the TransientComposite to be instantiated + * + * @return a TransientBuilder for creation of TransientComposites implementing the interface + * + * @throws NoSuchTransientException if no composite extending the mixinType has been registered + */ + <T> TransientBuilder<T> newTransientBuilder( Class<T> mixinType ) + throws NoSuchTransientException; + + /** + * Instantiate a TransientComposite of the given type. + * + * @param mixinType the TransientComposite type to instantiate + * + * @return a new TransientComposite instance + * + * @throws NoSuchTransientException if no composite extending the mixinType has been registered + * @throws org.apache.polygene.api.common.ConstructionException + * if the composite could not be instantiated + */ + <T> T newTransient( Class<T> mixinType, Object... uses ) + throws NoSuchTransientException, ConstructionException; +} http://git-wip-us.apache.org/repos/asf/zest-java/blob/1c722f44/core/api/src/main/java/org/apache/polygene/api/composite/TransientComposite.java ---------------------------------------------------------------------- diff --git a/core/api/src/main/java/org/apache/polygene/api/composite/TransientComposite.java b/core/api/src/main/java/org/apache/polygene/api/composite/TransientComposite.java new file mode 100644 index 0000000..bcf86fd --- /dev/null +++ b/core/api/src/main/java/org/apache/polygene/api/composite/TransientComposite.java @@ -0,0 +1,37 @@ +/* + * 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.polygene.api.composite; + +/** + * Transient Composite Type. + * + * TransientComposites have the following criteria; + * <ul> + * <li>Does not persist its state, and is not serializable</li> + * <li>Can not be referenced from Properties, Associations, ValueComposites nor Entities</li> + * <li>Can reference all types</li> + * <li>No lifecycle</li> + * <li>equals/hashCode is delegated to a single Mixin implementing the methods, like any other method</li> + * </ul> + */ +public interface TransientComposite + extends Composite +{ +} http://git-wip-us.apache.org/repos/asf/zest-java/blob/1c722f44/core/api/src/main/java/org/apache/polygene/api/composite/TransientDescriptor.java ---------------------------------------------------------------------- diff --git a/core/api/src/main/java/org/apache/polygene/api/composite/TransientDescriptor.java b/core/api/src/main/java/org/apache/polygene/api/composite/TransientDescriptor.java new file mode 100644 index 0000000..e4ce21a --- /dev/null +++ b/core/api/src/main/java/org/apache/polygene/api/composite/TransientDescriptor.java @@ -0,0 +1,29 @@ +/* + * 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.polygene.api.composite; + +/** + * TransientComposite Descriptor. + */ +public interface TransientDescriptor + extends CompositeDescriptor, StatefulCompositeDescriptor +{ +} http://git-wip-us.apache.org/repos/asf/zest-java/blob/1c722f44/core/api/src/main/java/org/apache/polygene/api/composite/package.html ---------------------------------------------------------------------- diff --git a/core/api/src/main/java/org/apache/polygene/api/composite/package.html b/core/api/src/main/java/org/apache/polygene/api/composite/package.html new file mode 100644 index 0000000..4c79ac8 --- /dev/null +++ b/core/api/src/main/java/org/apache/polygene/api/composite/package.html @@ -0,0 +1,24 @@ +<!-- + ~ 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. + ~ + ~ + --> +<html> + <body> + <h2>Composite API.</h2> + </body> +</html> http://git-wip-us.apache.org/repos/asf/zest-java/blob/1c722f44/core/api/src/main/java/org/apache/polygene/api/concern/ConcernDescriptor.java ---------------------------------------------------------------------- diff --git a/core/api/src/main/java/org/apache/polygene/api/concern/ConcernDescriptor.java b/core/api/src/main/java/org/apache/polygene/api/concern/ConcernDescriptor.java new file mode 100644 index 0000000..4986bec --- /dev/null +++ b/core/api/src/main/java/org/apache/polygene/api/concern/ConcernDescriptor.java @@ -0,0 +1,29 @@ +/* + * 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.polygene.api.concern; + +/** + * Concern descriptor. + */ +public interface ConcernDescriptor +{ + Class modifierClass(); +} http://git-wip-us.apache.org/repos/asf/zest-java/blob/1c722f44/core/api/src/main/java/org/apache/polygene/api/concern/ConcernOf.java ---------------------------------------------------------------------- diff --git a/core/api/src/main/java/org/apache/polygene/api/concern/ConcernOf.java b/core/api/src/main/java/org/apache/polygene/api/concern/ConcernOf.java new file mode 100644 index 0000000..e872308 --- /dev/null +++ b/core/api/src/main/java/org/apache/polygene/api/concern/ConcernOf.java @@ -0,0 +1,46 @@ +/* + * 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.polygene.api.concern; + +import org.apache.polygene.api.concern.internal.ConcernFor; + +/** + * Base class for Concerns. It introduces a typed "next" pointer + * that Concerns can use to invoke the next Concern (or mixin) in + * the chain. + * <p> + * Generic Concerns should subclass {@link GenericConcern} instead. + * </p> + * <p> + * Concerns implementations must be thread-safe in their implementation, + * as multiple threads may share instances. + * </p> + */ +public abstract class ConcernOf<T> +{ + /** + * The "next" pointer. This points to + * the next concern in the chain or the mixin + * to be invoked. + */ + final + @ConcernFor + protected T next = null; +} http://git-wip-us.apache.org/repos/asf/zest-java/blob/1c722f44/core/api/src/main/java/org/apache/polygene/api/concern/Concerns.java ---------------------------------------------------------------------- diff --git a/core/api/src/main/java/org/apache/polygene/api/concern/Concerns.java b/core/api/src/main/java/org/apache/polygene/api/concern/Concerns.java new file mode 100644 index 0000000..9c43e0f --- /dev/null +++ b/core/api/src/main/java/org/apache/polygene/api/concern/Concerns.java @@ -0,0 +1,39 @@ +/* + * 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.polygene.api.concern; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * This annotation is used by composites and mixins to declare what Concerns + * should be applied to the type or specific method. + */ +@Retention( RetentionPolicy.RUNTIME ) +@Target( { ElementType.TYPE, ElementType.METHOD } ) +@Documented +public @interface Concerns +{ + Class<?>[] value(); +} http://git-wip-us.apache.org/repos/asf/zest-java/blob/1c722f44/core/api/src/main/java/org/apache/polygene/api/concern/ConcernsDescriptor.java ---------------------------------------------------------------------- diff --git a/core/api/src/main/java/org/apache/polygene/api/concern/ConcernsDescriptor.java b/core/api/src/main/java/org/apache/polygene/api/concern/ConcernsDescriptor.java new file mode 100644 index 0000000..7a5ee07 --- /dev/null +++ b/core/api/src/main/java/org/apache/polygene/api/concern/ConcernsDescriptor.java @@ -0,0 +1,27 @@ +/* + * 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.polygene.api.concern; + +/** + * Concerns descriptor. + */ +public interface ConcernsDescriptor +{ +} http://git-wip-us.apache.org/repos/asf/zest-java/blob/1c722f44/core/api/src/main/java/org/apache/polygene/api/concern/GenericConcern.java ---------------------------------------------------------------------- diff --git a/core/api/src/main/java/org/apache/polygene/api/concern/GenericConcern.java b/core/api/src/main/java/org/apache/polygene/api/concern/GenericConcern.java new file mode 100644 index 0000000..f96aec0 --- /dev/null +++ b/core/api/src/main/java/org/apache/polygene/api/concern/GenericConcern.java @@ -0,0 +1,34 @@ +/* + * 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.polygene.api.concern; + +import java.lang.reflect.InvocationHandler; + +/** + * Base class for generic Concerns. Subclass + * and implement the "invoke" method. Use the + * "next" field in {@link ConcernOf} to continue the invocation + * chain. + */ +public abstract class GenericConcern + extends ConcernOf<InvocationHandler> + implements InvocationHandler +{ +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/zest-java/blob/1c722f44/core/api/src/main/java/org/apache/polygene/api/concern/internal/ConcernFor.java ---------------------------------------------------------------------- diff --git a/core/api/src/main/java/org/apache/polygene/api/concern/internal/ConcernFor.java b/core/api/src/main/java/org/apache/polygene/api/concern/internal/ConcernFor.java new file mode 100644 index 0000000..1db879c --- /dev/null +++ b/core/api/src/main/java/org/apache/polygene/api/concern/internal/ConcernFor.java @@ -0,0 +1,67 @@ +/* + * 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.polygene.api.concern.internal; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import org.apache.polygene.api.injection.InjectionScope; + +/** + * This annotation is required once in each Concern, to mark the + * field where the next element in the call sequence should be + * injected. + * <p> + * The type of the field must be of the same type as the Concern + * itself, or an InvocationHandler. + * </p> + * <p> + * Example; + * </p> + * <pre><code> + * public interface MyStuff + * { + * void doSomething(); + * } + * + * public class MyStuffConcern + * implements MyStuff + * { + * @ConcernFor MyStuff next; + * + * public void doSomething() + * { + * // HERE DO THE MODIFIER STUFF. + * + * // Delegate to the underlying mixin/modifier. + * next.doSomething(); + * } + * } + * </code></pre> + */ +@Retention( RetentionPolicy.RUNTIME ) +@Target( { ElementType.FIELD, ElementType.PARAMETER } ) +@Documented +@InjectionScope +public @interface ConcernFor +{ +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/zest-java/blob/1c722f44/core/api/src/main/java/org/apache/polygene/api/concern/internal/package.html ---------------------------------------------------------------------- diff --git a/core/api/src/main/java/org/apache/polygene/api/concern/internal/package.html b/core/api/src/main/java/org/apache/polygene/api/concern/internal/package.html new file mode 100644 index 0000000..7dcd3b6 --- /dev/null +++ b/core/api/src/main/java/org/apache/polygene/api/concern/internal/package.html @@ -0,0 +1,28 @@ +<!-- + ~ 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. + ~ + ~ + --> +<html> + <body> + <h1>Internal/Private package for the Concern API.</h1> + <p> + This is an internal package, and no classes in this package is part of the API and compatibility + with these classes will not be attempted. + </p> + </body> +</html> \ No newline at end of file http://git-wip-us.apache.org/repos/asf/zest-java/blob/1c722f44/core/api/src/main/java/org/apache/polygene/api/concern/package.html ---------------------------------------------------------------------- diff --git a/core/api/src/main/java/org/apache/polygene/api/concern/package.html b/core/api/src/main/java/org/apache/polygene/api/concern/package.html new file mode 100644 index 0000000..f9d5d0c --- /dev/null +++ b/core/api/src/main/java/org/apache/polygene/api/concern/package.html @@ -0,0 +1,24 @@ +<!-- + ~ 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. + ~ + ~ + --> +<html> + <body> + <h2>Concern API.</h2> + </body> +</html> http://git-wip-us.apache.org/repos/asf/zest-java/blob/1c722f44/core/api/src/main/java/org/apache/polygene/api/configuration/Configuration.java ---------------------------------------------------------------------- diff --git a/core/api/src/main/java/org/apache/polygene/api/configuration/Configuration.java b/core/api/src/main/java/org/apache/polygene/api/configuration/Configuration.java new file mode 100644 index 0000000..60da8eb --- /dev/null +++ b/core/api/src/main/java/org/apache/polygene/api/configuration/Configuration.java @@ -0,0 +1,401 @@ +/* + * 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.polygene.api.configuration; + +import java.io.IOException; +import java.io.InputStream; +import org.apache.polygene.api.PolygeneAPI; +import org.apache.polygene.api.composite.Composite; +import org.apache.polygene.api.composite.PropertyMapper; +import org.apache.polygene.api.constraint.ConstraintViolationException; +import org.apache.polygene.api.entity.EntityBuilder; +import org.apache.polygene.api.identity.HasIdentity; +import org.apache.polygene.api.identity.Identity; +import org.apache.polygene.api.injection.scope.Service; +import org.apache.polygene.api.injection.scope.Structure; +import org.apache.polygene.api.injection.scope.This; +import org.apache.polygene.api.mixin.Mixins; +import org.apache.polygene.api.service.ServiceComposite; +import org.apache.polygene.api.service.ServiceDescriptor; +import org.apache.polygene.api.service.ServiceReference; +import org.apache.polygene.api.service.qualifier.ServiceTags; +import org.apache.polygene.api.structure.Module; +import org.apache.polygene.api.unitofwork.NoSuchEntityTypeException; +import org.apache.polygene.api.unitofwork.NoSuchEntityException; +import org.apache.polygene.api.unitofwork.UnitOfWork; +import org.apache.polygene.api.unitofwork.UnitOfWorkCompletionException; +import org.apache.polygene.api.unitofwork.UnitOfWorkFactory; +import org.apache.polygene.api.usecase.Usecase; +import org.apache.polygene.api.usecase.UsecaseBuilder; +import org.apache.polygene.api.value.ValueSerialization; + +/** + * Provide Configurations for Services. A Service that wants to be configurable + * should inject a reference to Configuration with the Configuration type: + * <pre><code> + * * @This Configuration<MyServiceConfiguration> config; + * </code></pre> + * <p> + * where MyServiceConfiguration extends {@link ConfigurationComposite}, which itself is an ordinary + * {@link org.apache.polygene.api.entity.EntityComposite}. The Configuration implementation + * will either locate an instance of the given Configuration type in the + * persistent store using the reference of the Service, or create a new such instance + * if one doesn't already exist. + * </p> + * <p> + * If a new Configuration instance is created then it will be populated with properties + * from the properties file whose filesystem name is the same as the reference (e.g. "MyService.properties"). + * If a service is not given a name via the {@code org.apache.polygene.bootstrap.ServiceDeclaration#identifiedBy(String)}, the + * name will default to the FQCN of the ServiceComposite type. + * </p> + * <p> + * The Configuration instance can be modified externally just like any other EntityComposite, but + * its values will not be updated in the Service until {@link #refresh()} is called. This allows + * safe reloads of Configuration state to ensure that it is not reloaded while the Service is handling + * a request. + * </p> + * <p> + * The Configuration will be automatically refreshed when the Service is activated by the Polygene runtime. + * Any refreshes at other points will have to be done manually or triggered through some other + * mechanism. + * </p> + * <p> + * The user configuration entity is part of a long running {@link UnitOfWork}, and to persist changes to it the + * {@link #save()} method must be called. No other actions are required. Example; + * </p> + * <pre><code> + * + * public interface MyConfiguration extends ConfigurationComposite + * { + * Property<Long> timeout(); + * } + * + * : + * + * @This Configuration<MyConfiguration> config; + * : + * private void setTimeoutConfiguration( long timeout ) + * { + * config.get().timeout().set( timeout ); + * config.save(); + * } + * </code></pre> + * <p> + * And even if a separate thread is using the {@code timeout()} configuration when this is happening, the + * {@link UnitOfWork} isolation will ensure that the other thread is not affected. That thread, on the other hand + * will need to do a {@link #refresh()} at an appropriate time to pick up the timeout change. For instance; + * </p> + * <pre><code> + * + * @Service InventoryService remoteInventoryService; + * + * public void restockInventoryItem( InventoryItemId id, int itemCount ) + * { + * config.refresh(); + * long timeout = config.get().timeout().get(); + * + * remoteInventoryService.restock( id, itemCount, timeout ); + * + * : + * : + * } + * </code></pre> + */ +@SuppressWarnings( "JavadocReference" ) +@Mixins( Configuration.ConfigurationMixin.class ) +public interface Configuration<T> +{ + /** + * Retrieves the user configuration instance managed by this Configuration. + * <p> + * Even if the user configuration is initialized from properties file, the consistency rules of Polygene composites + * still applies. If the the properties file is missing a value, then the initialization will fail with a + * RuntimeException. If Constraints has been defined, those will need to be satisfied as well. The user + * configuration instance returned will fulfill the constraints and consistency normal to all composites, and + * can therefor safely be used with additional checks. + * </p> + * + * @return The fully initialized and ready-to-use user configuration instance. + */ + T get(); + + /** + * Updates the values of the managed user ConfigurationComposite instance from the underlying + * {@code org.apache.polygene.spi.entitystore.EntityStore}. Any modified values in the current user configuration that + * has not been saved, via {@link #save()} method, will be lost. + */ + void refresh(); + + /** + * Persists the modified values in the user configuration instance to the underlying store. + */ + void save(); + + /** + * Implementation of Configuration. + * <p> + * This is effectively an internal class in Polygene and should never be used directly by user code. + * </p> + * + * @param <T> + */ + public class ConfigurationMixin<T> + implements Configuration<T> + { + private T configuration; + private UnitOfWork uow; + + @Structure + private PolygeneAPI api; + + @This + private ServiceComposite me; + + @Structure + private UnitOfWorkFactory uowf; + + @Service + private Iterable<ServiceReference<ValueSerialization>> valueSerialization; + + + public ConfigurationMixin() + { + } + + @Override + public synchronized T get() + { + if( configuration == null ) + { + Usecase usecase = UsecaseBuilder.newUsecase( "Configuration:" + me.identity().get() ); + uow = uowf.newUnitOfWork( usecase ); + try + { + configuration = this.findConfigurationInstanceFor( me, uow ); + } + catch( InstantiationException e ) + { + throw new IllegalStateException( e ); + } + } + + return configuration; + } + + @Override + public synchronized void refresh() + { + if( configuration != null ) + { + configuration = null; + uow.discard(); + uow = null; + } + } + + @Override + public void save() + { + if( uow != null ) + { + try + { + uow.complete(); + uow = null; + } + catch( UnitOfWorkCompletionException e ) + { + // Should be impossible + e.printStackTrace(); + } + + configuration = null; // Force refresh + } + } + + @SuppressWarnings( "unchecked" ) + public <V> V findConfigurationInstanceFor( ServiceComposite serviceComposite, UnitOfWork uow ) + throws InstantiationException + { + ServiceDescriptor serviceModel = api.serviceDescriptorFor( serviceComposite ); + + V configuration; + try + { + configuration = uow.get( serviceModel.<V>configurationType(), serviceComposite.identity().get() ); + uow.pause(); + } + catch( NoSuchEntityException | NoSuchEntityTypeException e ) + { + return (V) initializeConfigurationInstance( serviceComposite, uow, serviceModel, serviceComposite.identity().get() ); + } + return configuration; + } + + @SuppressWarnings( "unchecked" ) + private <V extends HasIdentity> V initializeConfigurationInstance(ServiceComposite serviceComposite, + UnitOfWork uow, + ServiceDescriptor serviceModel, + Identity identity + ) + throws InstantiationException + { + Module module = api.moduleOf( serviceComposite ).instance(); + Usecase usecase = UsecaseBuilder.newUsecase( "Configuration:" + me.identity().get() ); + UnitOfWork buildUow = module.unitOfWorkFactory().newUnitOfWork( usecase ); + + Class<?> type = api.serviceDescriptorFor( serviceComposite ).types().findFirst().orElse( null ); + Class<V> configType = serviceModel.configurationType(); + + // Check for defaults + V config = tryLoadPropertiesFile( buildUow, type, configType, identity ); + if( config == null ) + { + config = tryLoadJsonFile( buildUow, type, configType, identity ); + if( config == null ) + { + config = tryLoadYamlFile( buildUow, type, configType, identity ); + if( config == null ) + { + config = tryLoadXmlFile( buildUow, type, configType, identity ); + if( config == null ) + { + try + { + EntityBuilder<V> configBuilder = buildUow.newEntityBuilder( serviceModel.<V>configurationType(), identity ); + configBuilder.newInstance(); + } + catch( ConstraintViolationException e ) + { + throw new NoSuchConfigurationException( configType, identity, e ); + } + } + } + } + } + + try + { + buildUow.complete(); + + // Try again + return (V) findConfigurationInstanceFor( serviceComposite, uow ); + } + catch( Exception e1 ) + { + InstantiationException ex = new InstantiationException( + "Could not instantiate configuration, and no configuration initialization file was found (" + identity + ")" ); + ex.initCause( e1 ); + throw ex; + } + } + + private <C, V> V tryLoadPropertiesFile( UnitOfWork buildUow, + Class<C> compositeType, + Class<V> configType, + Identity identity + ) + throws InstantiationException + { + EntityBuilder<V> configBuilder = buildUow.newEntityBuilder( configType, identity ); + String resourceName = identity + ".properties"; + InputStream asStream = getResource( compositeType, resourceName ); + if( asStream != null ) + { + try + { + PropertyMapper.map( asStream, (Composite) configBuilder.instance() ); + return configBuilder.newInstance(); + } + catch( IOException e1 ) + { + InstantiationException exception = new InstantiationException( + "Could not read underlying Properties file." ); + exception.initCause( e1 ); + throw exception; + } + } + return null; + } + + private InputStream getResource( Class<?> type, String resourceName ) + { + // Load defaults from classpath root if available + if( type.getResource( resourceName ) == null && type.getResource( "/" + resourceName ) != null ) + { + resourceName = "/" + resourceName; + } + return type.getResourceAsStream( resourceName ); + } + + private <C, V extends HasIdentity> V tryLoadJsonFile(UnitOfWork uow, + Class<C> compositeType, + Class<V> configType, + Identity identity + ) + { + return readConfig( uow, compositeType, configType, identity, ValueSerialization.Formats.JSON, ".json" ); + } + + private <C, V extends HasIdentity> V tryLoadYamlFile(UnitOfWork uow, + Class<C> compositeType, + Class<V> configType, + Identity identity + ) + { + return readConfig( uow, compositeType, configType, identity, ValueSerialization.Formats.YAML, ".yaml" ); + } + + private <C, V extends HasIdentity> V tryLoadXmlFile(UnitOfWork uow, + Class<C> compositeType, + Class<V> configType, + Identity identity + ) + { + return readConfig( uow, compositeType, configType, identity, ValueSerialization.Formats.XML, ".xml" ); + } + + private <C, V extends HasIdentity> V readConfig(UnitOfWork uow, + Class<C> compositeType, + Class<V> configType, + Identity identity, + String format, + String extension + ) + { + for( ServiceReference<ValueSerialization> serializerRef : valueSerialization ) + { + ServiceTags serviceTags = serializerRef.metaInfo( ServiceTags.class ); + if( serviceTags.hasTag( format ) ) + { + String resourceName = identity + extension; + InputStream asStream = getResource( compositeType, resourceName ); + if( asStream != null ) + { + V configObject = serializerRef.get().deserialize( uow.module(), configType, asStream ); + return uow.toEntity( configType, configObject ); + } + } + } + return null; + } + } +}
