You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@polygene.apache.org by ni...@apache.org on 2015/07/31 04:59:28 UTC
[48/81] [abbrv] [partial] zest-java git commit: First round of
changes to move to org.apache.zest namespace.
http://git-wip-us.apache.org/repos/asf/zest-java/blob/061ddaa0/core/api/src/main/java/org/apache/zest/api/composite/PropertyMapper.java
----------------------------------------------------------------------
diff --git a/core/api/src/main/java/org/apache/zest/api/composite/PropertyMapper.java b/core/api/src/main/java/org/apache/zest/api/composite/PropertyMapper.java
new file mode 100644
index 0000000..92f2e7b
--- /dev/null
+++ b/core/api/src/main/java/org/apache/zest/api/composite/PropertyMapper.java
@@ -0,0 +1,580 @@
+/*
+ * 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.zest.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.util.ArrayList;
+import java.util.Collection;
+import java.util.Date;
+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.zest.api.Qi4j;
+import org.apache.zest.api.property.GenericPropertyInfo;
+import org.apache.zest.api.property.Property;
+import org.apache.zest.api.util.Classes;
+import org.apache.zest.api.util.Dates;
+import org.apache.zest.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( Date.class, new DateMapper() );
+ 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.map( 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 DateMapper
+ implements MappingStrategy
+ {
+ @Override
+ public Object map( Composite composite, Type type, String value )
+ {
+ return Dates.fromString( value.trim() );
+ }
+ }
+
+ private static class ValueCompositeMapper
+ implements MappingStrategy
+ {
+ @Override
+ @SuppressWarnings( "unchecked" )
+ public Object map( Composite composite, Type type, String value )
+ {
+ return Qi4j.FUNCTION_COMPOSITE_INSTANCE_OF.map( composite ).module().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/061ddaa0/core/api/src/main/java/org/apache/zest/api/composite/StateDescriptor.java
----------------------------------------------------------------------
diff --git a/core/api/src/main/java/org/apache/zest/api/composite/StateDescriptor.java b/core/api/src/main/java/org/apache/zest/api/composite/StateDescriptor.java
new file mode 100644
index 0000000..2d3ac07
--- /dev/null
+++ b/core/api/src/main/java/org/apache/zest/api/composite/StateDescriptor.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2008, Rickard Öberg. All Rights Reserved.
+ *
+ * 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.
+ *
+ */
+
+package org.apache.zest.api.composite;
+
+import org.apache.zest.api.common.QualifiedName;
+import org.apache.zest.api.property.PropertyDescriptor;
+
+/**
+ * Composite State Descriptor.
+ */
+public interface StateDescriptor
+{
+ PropertyDescriptor findPropertyModelByName( String name )
+ throws IllegalArgumentException;
+
+ PropertyDescriptor findPropertyModelByQualifiedName( QualifiedName name )
+ throws IllegalArgumentException;
+
+ Iterable<? extends PropertyDescriptor> properties();
+}
http://git-wip-us.apache.org/repos/asf/zest-java/blob/061ddaa0/core/api/src/main/java/org/apache/zest/api/composite/StatefulCompositeDescriptor.java
----------------------------------------------------------------------
diff --git a/core/api/src/main/java/org/apache/zest/api/composite/StatefulCompositeDescriptor.java b/core/api/src/main/java/org/apache/zest/api/composite/StatefulCompositeDescriptor.java
new file mode 100644
index 0000000..58c38f9
--- /dev/null
+++ b/core/api/src/main/java/org/apache/zest/api/composite/StatefulCompositeDescriptor.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.zest.api.composite;
+
+/**
+ * Stateful Composite Descriptor.
+ */
+public interface StatefulCompositeDescriptor
+{
+ StateDescriptor state();
+}
http://git-wip-us.apache.org/repos/asf/zest-java/blob/061ddaa0/core/api/src/main/java/org/apache/zest/api/composite/TransientBuilder.java
----------------------------------------------------------------------
diff --git a/core/api/src/main/java/org/apache/zest/api/composite/TransientBuilder.java b/core/api/src/main/java/org/apache/zest/api/composite/TransientBuilder.java
new file mode 100644
index 0000000..b54682e
--- /dev/null
+++ b/core/api/src/main/java/org/apache/zest/api/composite/TransientBuilder.java
@@ -0,0 +1,68 @@
+/* Copyright 2007 Niclas Hedhman.
+ *
+ * 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.
+ */
+package org.apache.zest.api.composite;
+
+import org.apache.zest.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.zest.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/061ddaa0/core/api/src/main/java/org/apache/zest/api/composite/TransientBuilderFactory.java
----------------------------------------------------------------------
diff --git a/core/api/src/main/java/org/apache/zest/api/composite/TransientBuilderFactory.java b/core/api/src/main/java/org/apache/zest/api/composite/TransientBuilderFactory.java
new file mode 100644
index 0000000..0e64cde
--- /dev/null
+++ b/core/api/src/main/java/org/apache/zest/api/composite/TransientBuilderFactory.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2007, Rickard Öberg. All Rights Reserved.
+ * Copyright (c) 2007, Niclas Hedhman. All Rights Reserved.
+ *
+ * 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.
+ *
+ */
+package org.apache.zest.api.composite;
+
+import org.apache.zest.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.zest.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/061ddaa0/core/api/src/main/java/org/apache/zest/api/composite/TransientComposite.java
----------------------------------------------------------------------
diff --git a/core/api/src/main/java/org/apache/zest/api/composite/TransientComposite.java b/core/api/src/main/java/org/apache/zest/api/composite/TransientComposite.java
new file mode 100644
index 0000000..f3b1952
--- /dev/null
+++ b/core/api/src/main/java/org/apache/zest/api/composite/TransientComposite.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2009 Niclas Hedhman.
+ *
+ * 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.
+ */
+package org.apache.zest.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/061ddaa0/core/api/src/main/java/org/apache/zest/api/composite/TransientDescriptor.java
----------------------------------------------------------------------
diff --git a/core/api/src/main/java/org/apache/zest/api/composite/TransientDescriptor.java b/core/api/src/main/java/org/apache/zest/api/composite/TransientDescriptor.java
new file mode 100644
index 0000000..ec5bf24
--- /dev/null
+++ b/core/api/src/main/java/org/apache/zest/api/composite/TransientDescriptor.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright (c) 2008, Rickard Öberg. All Rights Reserved.
+ *
+ * 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.
+ *
+ */
+
+package org.apache.zest.api.composite;
+
+/**
+ * TransientComposite Descriptor.
+ */
+public interface TransientDescriptor
+ extends CompositeDescriptor, StatefulCompositeDescriptor
+{
+}
http://git-wip-us.apache.org/repos/asf/zest-java/blob/061ddaa0/core/api/src/main/java/org/apache/zest/api/composite/package.html
----------------------------------------------------------------------
diff --git a/core/api/src/main/java/org/apache/zest/api/composite/package.html b/core/api/src/main/java/org/apache/zest/api/composite/package.html
new file mode 100644
index 0000000..00feaed
--- /dev/null
+++ b/core/api/src/main/java/org/apache/zest/api/composite/package.html
@@ -0,0 +1,21 @@
+<!--
+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/061ddaa0/core/api/src/main/java/org/apache/zest/api/concern/ConcernDescriptor.java
----------------------------------------------------------------------
diff --git a/core/api/src/main/java/org/apache/zest/api/concern/ConcernDescriptor.java b/core/api/src/main/java/org/apache/zest/api/concern/ConcernDescriptor.java
new file mode 100644
index 0000000..05ac497
--- /dev/null
+++ b/core/api/src/main/java/org/apache/zest/api/concern/ConcernDescriptor.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright (c) 2008, Rickard Öberg. All Rights Reserved.
+ *
+ * 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.
+ *
+ */
+
+package org.apache.zest.api.concern;
+
+/**
+ * Concern descriptor.
+ */
+public interface ConcernDescriptor
+{
+ Class modifierClass();
+}
http://git-wip-us.apache.org/repos/asf/zest-java/blob/061ddaa0/core/api/src/main/java/org/apache/zest/api/concern/ConcernOf.java
----------------------------------------------------------------------
diff --git a/core/api/src/main/java/org/apache/zest/api/concern/ConcernOf.java b/core/api/src/main/java/org/apache/zest/api/concern/ConcernOf.java
new file mode 100644
index 0000000..24e4faa
--- /dev/null
+++ b/core/api/src/main/java/org/apache/zest/api/concern/ConcernOf.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2008 Niclas Hedhman. All rights Reserved.
+ *
+ * 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.
+ */
+package org.apache.zest.api.concern;
+
+import org.apache.zest.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/061ddaa0/core/api/src/main/java/org/apache/zest/api/concern/Concerns.java
----------------------------------------------------------------------
diff --git a/core/api/src/main/java/org/apache/zest/api/concern/Concerns.java b/core/api/src/main/java/org/apache/zest/api/concern/Concerns.java
new file mode 100644
index 0000000..d871fc1
--- /dev/null
+++ b/core/api/src/main/java/org/apache/zest/api/concern/Concerns.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2007, Rickard Öberg. All Rights Reserved.
+ * Copyright (c) 2007, Niclas Hedhman. All Rights Reserved.
+ *
+ * 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.
+ *
+ */
+
+package org.apache.zest.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/061ddaa0/core/api/src/main/java/org/apache/zest/api/concern/ConcernsDescriptor.java
----------------------------------------------------------------------
diff --git a/core/api/src/main/java/org/apache/zest/api/concern/ConcernsDescriptor.java b/core/api/src/main/java/org/apache/zest/api/concern/ConcernsDescriptor.java
new file mode 100644
index 0000000..261c18b
--- /dev/null
+++ b/core/api/src/main/java/org/apache/zest/api/concern/ConcernsDescriptor.java
@@ -0,0 +1,24 @@
+/* Copyright 2008 Edward Yakop.
+*
+* 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.
+*/
+package org.apache.zest.api.concern;
+
+/**
+ * Concerns descriptor.
+ */
+public interface ConcernsDescriptor
+{
+}
http://git-wip-us.apache.org/repos/asf/zest-java/blob/061ddaa0/core/api/src/main/java/org/apache/zest/api/concern/GenericConcern.java
----------------------------------------------------------------------
diff --git a/core/api/src/main/java/org/apache/zest/api/concern/GenericConcern.java b/core/api/src/main/java/org/apache/zest/api/concern/GenericConcern.java
new file mode 100644
index 0000000..33777bc
--- /dev/null
+++ b/core/api/src/main/java/org/apache/zest/api/concern/GenericConcern.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2008 Niclas Hedhman. All rights Reserved.
+ *
+ * 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.
+ */
+package org.apache.zest.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/061ddaa0/core/api/src/main/java/org/apache/zest/api/concern/internal/ConcernFor.java
----------------------------------------------------------------------
diff --git a/core/api/src/main/java/org/apache/zest/api/concern/internal/ConcernFor.java b/core/api/src/main/java/org/apache/zest/api/concern/internal/ConcernFor.java
new file mode 100644
index 0000000..dc5169d
--- /dev/null
+++ b/core/api/src/main/java/org/apache/zest/api/concern/internal/ConcernFor.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2007, Rickard Öberg. All Rights Reserved.
+ * Copyright (c) 2007, Niclas Hedhman. All Rights Reserved.
+ *
+ * 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.
+ *
+ */
+package org.apache.zest.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.zest.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/061ddaa0/core/api/src/main/java/org/apache/zest/api/concern/internal/package.html
----------------------------------------------------------------------
diff --git a/core/api/src/main/java/org/apache/zest/api/concern/internal/package.html b/core/api/src/main/java/org/apache/zest/api/concern/internal/package.html
new file mode 100644
index 0000000..9351f10
--- /dev/null
+++ b/core/api/src/main/java/org/apache/zest/api/concern/internal/package.html
@@ -0,0 +1,25 @@
+<!--
+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/061ddaa0/core/api/src/main/java/org/apache/zest/api/concern/package.html
----------------------------------------------------------------------
diff --git a/core/api/src/main/java/org/apache/zest/api/concern/package.html b/core/api/src/main/java/org/apache/zest/api/concern/package.html
new file mode 100644
index 0000000..fcc7ef7
--- /dev/null
+++ b/core/api/src/main/java/org/apache/zest/api/concern/package.html
@@ -0,0 +1,21 @@
+<!--
+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/061ddaa0/core/api/src/main/java/org/apache/zest/api/configuration/Configuration.java
----------------------------------------------------------------------
diff --git a/core/api/src/main/java/org/apache/zest/api/configuration/Configuration.java b/core/api/src/main/java/org/apache/zest/api/configuration/Configuration.java
new file mode 100644
index 0000000..9125dac
--- /dev/null
+++ b/core/api/src/main/java/org/apache/zest/api/configuration/Configuration.java
@@ -0,0 +1,396 @@
+/*
+ * Copyright (c) 2008, Rickard Öberg. All Rights Reserved.
+ * Copyright (c) 2012, Paul Merlin.
+ *
+ * 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.
+ *
+ */
+
+package org.apache.zest.api.configuration;
+
+import java.io.IOException;
+import java.io.InputStream;
+import org.apache.zest.api.Qi4j;
+import org.apache.zest.api.composite.Composite;
+import org.apache.zest.api.composite.PropertyMapper;
+import org.apache.zest.api.constraint.ConstraintViolationException;
+import org.apache.zest.api.entity.EntityBuilder;
+import org.apache.zest.api.entity.Identity;
+import org.apache.zest.api.injection.scope.Service;
+import org.apache.zest.api.injection.scope.Structure;
+import org.apache.zest.api.injection.scope.This;
+import org.apache.zest.api.mixin.Mixins;
+import org.apache.zest.api.service.ServiceComposite;
+import org.apache.zest.api.service.ServiceDescriptor;
+import org.apache.zest.api.service.ServiceReference;
+import org.apache.zest.api.service.qualifier.ServiceTags;
+import org.apache.zest.api.structure.Module;
+import org.apache.zest.api.unitofwork.EntityTypeNotFoundException;
+import org.apache.zest.api.unitofwork.NoSuchEntityException;
+import org.apache.zest.api.unitofwork.UnitOfWork;
+import org.apache.zest.api.unitofwork.UnitOfWorkCompletionException;
+import org.apache.zest.api.usecase.Usecase;
+import org.apache.zest.api.usecase.UsecaseBuilder;
+import org.apache.zest.api.value.ValueSerialization;
+
+import static org.apache.zest.functional.Iterables.first;
+
+/**
+ * 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.zest.api.entity.EntityComposite}. The Configuration implementation
+ * will either locate an instance of the given Configuration type in the
+ * persistent store using the identity 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 identity (e.g. "MyService.properties").
+ * If a service is not given a name via the {@code org.qi4j.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 Zest 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 Zest 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.qi4j.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 Zest 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 Qi4j api;
+
+ @This
+ private ServiceComposite me;
+
+ @Structure
+ private Module module;
+
+ @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 = module.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 );
+
+ String identity = serviceComposite.identity().get();
+ V configuration;
+ try
+ {
+ configuration = uow.get( serviceModel.<V>configurationType(), identity );
+ uow.pause();
+ }
+ catch( NoSuchEntityException | EntityTypeNotFoundException e )
+ {
+ return (V) initializeConfigurationInstance( serviceComposite, uow, serviceModel, identity );
+ }
+ return configuration;
+ }
+
+ @SuppressWarnings( "unchecked" )
+ private <V extends Identity> V initializeConfigurationInstance( ServiceComposite serviceComposite,
+ UnitOfWork uow,
+ ServiceDescriptor serviceModel,
+ String identity
+ )
+ throws InstantiationException
+ {
+ Module module = api.moduleOf( serviceComposite );
+ Usecase usecase = UsecaseBuilder.newUsecase( "Configuration:" + me.identity().get() );
+ UnitOfWork buildUow = module.newUnitOfWork( usecase );
+
+ Class<?> type = first( api.serviceDescriptorFor( serviceComposite ).types() );
+ 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,
+ String 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 Identity> V tryLoadJsonFile( UnitOfWork uow,
+ Class<C> compositeType,
+ Class<V> configType,
+ String identity
+ )
+ {
+ return readConfig( uow, compositeType, configType, identity, ValueSerialization.Formats.JSON, ".json" );
+ }
+
+ private <C, V extends Identity> V tryLoadYamlFile( UnitOfWork uow,
+ Class<C> compositeType,
+ Class<V> configType,
+ String identity
+ )
+ {
+ return readConfig( uow, compositeType, configType, identity, ValueSerialization.Formats.YAML, ".yaml" );
+ }
+
+ private <C, V extends Identity> V tryLoadXmlFile( UnitOfWork uow,
+ Class<C> compositeType,
+ Class<V> configType,
+ String identity
+ )
+ {
+ return readConfig( uow, compositeType, configType, identity, ValueSerialization.Formats.XML, ".xml" );
+ }
+
+ private <C, V extends Identity> V readConfig( UnitOfWork uow,
+ Class<C> compositeType,
+ Class<V> configType,
+ String 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( configType, asStream );
+ return uow.toEntity( configType, configObject );
+ }
+ }
+ }
+ return null;
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/zest-java/blob/061ddaa0/core/api/src/main/java/org/apache/zest/api/configuration/ConfigurationComposite.java
----------------------------------------------------------------------
diff --git a/core/api/src/main/java/org/apache/zest/api/configuration/ConfigurationComposite.java b/core/api/src/main/java/org/apache/zest/api/configuration/ConfigurationComposite.java
new file mode 100644
index 0000000..881e52a
--- /dev/null
+++ b/core/api/src/main/java/org/apache/zest/api/configuration/ConfigurationComposite.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2008, Rickard Öberg. All Rights Reserved.
+ *
+ * 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.
+ *
+ */
+
+package org.apache.zest.api.configuration;
+
+import org.apache.zest.api.composite.Composite;
+import org.apache.zest.api.entity.Identity;
+import org.apache.zest.api.entity.Queryable;
+
+/**
+ * Services that want to be configurable should have a ConfigurationComposite that contains all the settings.
+ * They are treated as EntityComposites, and are therefore stored in an EntityStore. There will be one instance
+ * per service instance that uses each ConfigurationComposite, and the identity of the entity is the same as that
+ * of the service.
+ */
+@Queryable( false )
+public interface ConfigurationComposite
+ extends Identity, Composite
+{
+}
http://git-wip-us.apache.org/repos/asf/zest-java/blob/061ddaa0/core/api/src/main/java/org/apache/zest/api/configuration/Enabled.java
----------------------------------------------------------------------
diff --git a/core/api/src/main/java/org/apache/zest/api/configuration/Enabled.java b/core/api/src/main/java/org/apache/zest/api/configuration/Enabled.java
new file mode 100644
index 0000000..4eb8cc7
--- /dev/null
+++ b/core/api/src/main/java/org/apache/zest/api/configuration/Enabled.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2010, Rickard Öberg. All Rights Reserved.
+ *
+ * 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.
+ *
+ */
+
+package org.apache.zest.api.configuration;
+
+import org.apache.zest.api.common.UseDefaults;
+import org.apache.zest.api.property.Property;
+
+/**
+ * Common configuration for setting whether a service is enabled or not. A disabled service
+ * is not considered to be available. Let your own ConfigurationComposite extend this interface to use.
+ */
+public interface Enabled
+{
+ @UseDefaults
+ Property<Boolean> enabled();
+}
http://git-wip-us.apache.org/repos/asf/zest-java/blob/061ddaa0/core/api/src/main/java/org/apache/zest/api/configuration/NoSuchConfigurationException.java
----------------------------------------------------------------------
diff --git a/core/api/src/main/java/org/apache/zest/api/configuration/NoSuchConfigurationException.java b/core/api/src/main/java/org/apache/zest/api/configuration/NoSuchConfigurationException.java
new file mode 100644
index 0000000..c3ad366
--- /dev/null
+++ b/core/api/src/main/java/org/apache/zest/api/configuration/NoSuchConfigurationException.java
@@ -0,0 +1,48 @@
+/*
+ * 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.zest.api.configuration;
+
+import org.apache.zest.api.constraint.ConstraintViolationException;
+import org.apache.zest.api.entity.Identity;
+
+public class NoSuchConfigurationException extends RuntimeException
+{
+ private final Class<? extends Identity> configType;
+ private final String identity;
+
+ public NoSuchConfigurationException( Class<? extends Identity> configType,
+ String identity,
+ ConstraintViolationException cause
+ )
+ {
+ super( "No configuration found for '" + identity + "' and configuration " + configType.getName() + " has one or more non-Optional properties.", cause );
+ this.configType = configType;
+ this.identity = identity;
+ }
+
+ public Class<? extends Identity> configType()
+ {
+ return configType;
+ }
+
+ public String identity()
+ {
+ return identity;
+ }
+}
http://git-wip-us.apache.org/repos/asf/zest-java/blob/061ddaa0/core/api/src/main/java/org/apache/zest/api/configuration/package.html
----------------------------------------------------------------------
diff --git a/core/api/src/main/java/org/apache/zest/api/configuration/package.html b/core/api/src/main/java/org/apache/zest/api/configuration/package.html
new file mode 100644
index 0000000..7f8a892
--- /dev/null
+++ b/core/api/src/main/java/org/apache/zest/api/configuration/package.html
@@ -0,0 +1,21 @@
+<!--
+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>Configuration API.</h2>
+ </body>
+</html>
http://git-wip-us.apache.org/repos/asf/zest-java/blob/061ddaa0/core/api/src/main/java/org/apache/zest/api/constraint/Constraint.java
----------------------------------------------------------------------
diff --git a/core/api/src/main/java/org/apache/zest/api/constraint/Constraint.java b/core/api/src/main/java/org/apache/zest/api/constraint/Constraint.java
new file mode 100644
index 0000000..9fb4f22
--- /dev/null
+++ b/core/api/src/main/java/org/apache/zest/api/constraint/Constraint.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2007, Rickard Öberg. All Rights Reserved.
+ *
+ * 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.
+ *
+ */
+
+package org.apache.zest.api.constraint;
+
+import java.io.Serializable;
+import java.lang.annotation.Annotation;
+
+/**
+ * All Constraints must implement this interface, which is used for each
+ * value validation.
+ */
+public interface Constraint<ANNOTATION extends Annotation, TYPE>
+ extends Serializable
+{
+ /**
+ * For each value or parameter which should be checked this method will be invoked.
+ * If the method returns true the value is valid. If it returns false the value
+ * is considered invalid. When all constraints have been checked a ConstraintViolationException
+ * will be thrown with all the constraint violations that were found.
+ *
+ * @param annotation the annotation to match
+ * @param value the value to be checked
+ *
+ * @return true if valid, false if invalid
+ */
+ boolean isValid( ANNOTATION annotation, TYPE value );
+}
http://git-wip-us.apache.org/repos/asf/zest-java/blob/061ddaa0/core/api/src/main/java/org/apache/zest/api/constraint/ConstraintDeclaration.java
----------------------------------------------------------------------
diff --git a/core/api/src/main/java/org/apache/zest/api/constraint/ConstraintDeclaration.java b/core/api/src/main/java/org/apache/zest/api/constraint/ConstraintDeclaration.java
new file mode 100644
index 0000000..9a74935
--- /dev/null
+++ b/core/api/src/main/java/org/apache/zest/api/constraint/ConstraintDeclaration.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2007, Rickard Öberg. All Rights Reserved.
+ *
+ * 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.
+ *
+ */
+
+package org.apache.zest.api.constraint;
+
+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;
+
+/**
+ * All annotations that are used to trigger Constraints must have this annotation.
+ */
+@Retention( RetentionPolicy.RUNTIME )
+@Target( ElementType.ANNOTATION_TYPE )
+@Documented
+public @interface ConstraintDeclaration
+{
+}
http://git-wip-us.apache.org/repos/asf/zest-java/blob/061ddaa0/core/api/src/main/java/org/apache/zest/api/constraint/ConstraintDescriptor.java
----------------------------------------------------------------------
diff --git a/core/api/src/main/java/org/apache/zest/api/constraint/ConstraintDescriptor.java b/core/api/src/main/java/org/apache/zest/api/constraint/ConstraintDescriptor.java
new file mode 100644
index 0000000..aa08d58
--- /dev/null
+++ b/core/api/src/main/java/org/apache/zest/api/constraint/ConstraintDescriptor.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2008, Rickard Öberg. All Rights Reserved.
+ *
+ * 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.
+ *
+ */
+
+package org.apache.zest.api.constraint;
+
+import java.lang.annotation.Annotation;
+
+/**
+ * Constraint Descriptor.
+ */
+public interface ConstraintDescriptor
+{
+ Annotation annotation();
+}
http://git-wip-us.apache.org/repos/asf/zest-java/blob/061ddaa0/core/api/src/main/java/org/apache/zest/api/constraint/ConstraintImplementationNotFoundException.java
----------------------------------------------------------------------
diff --git a/core/api/src/main/java/org/apache/zest/api/constraint/ConstraintImplementationNotFoundException.java b/core/api/src/main/java/org/apache/zest/api/constraint/ConstraintImplementationNotFoundException.java
new file mode 100644
index 0000000..e66a90a
--- /dev/null
+++ b/core/api/src/main/java/org/apache/zest/api/constraint/ConstraintImplementationNotFoundException.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2008, Niclas Hedhman. All Rights Reserved.
+ *
+ * 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.
+ *
+ */
+package org.apache.zest.api.constraint;
+
+import org.apache.zest.api.common.InvalidApplicationException;
+
+/**
+ * This exception is thrown if a Constraint implementation can not be found.
+ */
+public class ConstraintImplementationNotFoundException
+ extends InvalidApplicationException
+{
+ public ConstraintImplementationNotFoundException( String message )
+ {
+ super( message );
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/zest-java/blob/061ddaa0/core/api/src/main/java/org/apache/zest/api/constraint/ConstraintViolation.java
----------------------------------------------------------------------
diff --git a/core/api/src/main/java/org/apache/zest/api/constraint/ConstraintViolation.java b/core/api/src/main/java/org/apache/zest/api/constraint/ConstraintViolation.java
new file mode 100644
index 0000000..4ecf027
--- /dev/null
+++ b/core/api/src/main/java/org/apache/zest/api/constraint/ConstraintViolation.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2007, Rickard Öberg. All Rights Reserved.
+ *
+ * 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.
+ *
+ */
+
+package org.apache.zest.api.constraint;
+
+import java.io.Serializable;
+import java.lang.annotation.Annotation;
+
+/**
+ * When a constraint violation has occurred (ie Constraint.isValid has returned false) it
+ * is put in a collection of all violations that have occurred for this value check.
+ */
+public final class ConstraintViolation
+ implements Serializable
+{
+ private String name;
+ private final Annotation constraint;
+ private final Object value;
+
+ public ConstraintViolation( String name, Annotation constraint, Object value )
+ {
+ this.name = name;
+ this.constraint = constraint;
+ this.value = value;
+ }
+
+ public String name()
+ {
+ return name;
+ }
+
+ public Annotation constraint()
+ {
+ return constraint;
+ }
+
+ public Object value()
+ {
+ return value;
+ }
+}
http://git-wip-us.apache.org/repos/asf/zest-java/blob/061ddaa0/core/api/src/main/java/org/apache/zest/api/constraint/ConstraintViolationException.java
----------------------------------------------------------------------
diff --git a/core/api/src/main/java/org/apache/zest/api/constraint/ConstraintViolationException.java b/core/api/src/main/java/org/apache/zest/api/constraint/ConstraintViolationException.java
new file mode 100644
index 0000000..67c0702
--- /dev/null
+++ b/core/api/src/main/java/org/apache/zest/api/constraint/ConstraintViolationException.java
@@ -0,0 +1,257 @@
+/*
+ * Copyright 2008 Niclas Hedhman. All rights Reserved.
+ *
+ * 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.
+ */
+package org.apache.zest.api.constraint;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Member;
+import java.text.MessageFormat;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Locale;
+import java.util.MissingResourceException;
+import java.util.ResourceBundle;
+import org.apache.zest.api.Qi4j;
+import org.apache.zest.api.composite.Composite;
+import org.apache.zest.functional.Function;
+import org.apache.zest.functional.Iterables;
+
+/**
+ * This Exception is thrown when there is one or more Constraint Violations in a method
+ * call.
+ * <p>
+ * The Constraint Violations are aggregated per method, and this exception will contain those
+ * violations, together with the Composite instance it happened on as well as the Method that
+ * was invoked. The Exception also has support for localized messages of these violations.
+ * </p>
+ */
+public class ConstraintViolationException
+ extends IllegalArgumentException
+{
+ private static final long serialVersionUID = 1L;
+
+ private final Collection<ConstraintViolation> constraintViolations;
+ private String methodName;
+ private String mixinTypeName;
+ private String instanceToString;
+ private Iterable<Class<?>> instanceTypes;
+
+ public ConstraintViolationException( Composite instance, Member method,
+ Collection<ConstraintViolation> constraintViolations
+ )
+ {
+ this( instance.toString(), Qi4j.FUNCTION_DESCRIPTOR_FOR.map( instance ).types(), method, constraintViolations );
+ }
+
+ public ConstraintViolationException( String instanceToString,
+ Iterable<Class<?>> instanceTypes,
+ Member method,
+ Collection<ConstraintViolation> violations
+ )
+ {
+ this.instanceToString = instanceToString;
+ this.instanceTypes = instanceTypes;
+ mixinTypeName = method.getDeclaringClass().getName();
+ methodName = method.getName();
+ this.constraintViolations = violations;
+ }
+
+ public ConstraintViolationException( String instanceToString,
+ Iterable<Class<?>> instanceTypes,
+ String mixinTypeName,
+ String methodName,
+ Collection<ConstraintViolation> violations
+ )
+ {
+ this.instanceToString = instanceToString;
+ this.instanceTypes = instanceTypes;
+ this.mixinTypeName = mixinTypeName;
+ this.methodName = methodName;
+ this.constraintViolations = violations;
+ }
+
+ public Collection<ConstraintViolation> constraintViolations()
+ {
+ return constraintViolations;
+ }
+
+ /**
+ * Creates localized messages of all the constraint violations that has occured.
+ * <p>
+ * The key "<code>Qi4j_ConstraintViolation_<i><strong>CompositeType</strong></i></code>" will be used to lookup the text formatting
+ * pattern from the ResourceBundle, where <strong><code><i>CompositeType</i></code></strong> is the
+ * class name of the Composite where the constraint was violated. If such key does not exist, then the
+ * key "<code>Qi4j_ConstraintViolation</code>" will be used, and if that one also doesn't exist, or
+ * the resourceBundle argument is null, then the default patterns will be used;
+ * </p>
+ * <table summary="Localization of constraint vioations.">
+ * <tr><th>Type of Composite</th><th>Pattern used</th></tr>
+ * <tr><td>Composite</td>
+ * <td><code>Constraint Violation in {2}.{3} with constraint {4}, in composite \n{0} of type {1}</code></td>
+ * </tr>
+ * <tr><td>EntityComposite</td>
+ * <td><code>Constraint Violation in {2}.{3} with constraint {4}, in entity {1}[id={0}]</code></td>
+ * </tr>
+ * <tr><td>ServiceComposite</td>
+ * <td><code>Constraint Violation in {2}.{3} with constraint {4}, in service {0}</code></td>
+ * </tr>
+ * </table>
+ * Then format each ConstraintViolation according to such pattern, where the following argument are passed;
+ * <table summary="List of arguments available."><tr><th>Arg</th><th>Value</th></tr>
+ * <tr>
+ * <td>{0}</td>
+ * <td>Composite instance toString()</td>
+ * </tr>
+ * <tr>
+ * <td>{1}</td>
+ * <td>CompositeType class name</td>
+ * </tr>
+ * <tr>
+ * <td>{2}</td>
+ * <td>MixinType class name</td>
+ * </tr>
+ * <tr>
+ * <td>{3}</td>
+ * <td>MixinType method name</td>
+ * </tr>
+ * <tr>
+ * <td>{4}</td>
+ * <td>Annotation toString()</td>
+ * </tr>
+ * <tr>
+ * <td>{5}</td>
+ * <td>toString() of value passed as the argument, or "null" text if argument was null.</td>
+ * </tr>
+ * </table>
+ * <p>
+ * <b>NOTE!!!</b> This class is still under construction and will be modified further.
+ * </p>
+ *
+ * @param bundle The ResourceBundle for Localization, or null if default formatting and locale to be used.
+ *
+ * @return An array of localized messages of the violations incurred.
+ */
+ public String[] localizedMessagesFrom( ResourceBundle bundle )
+ {
+ String pattern = "Constraint violation in {0}.{1} for method ''{3}'' with constraint \"{4}({6})\", for value ''{5}''";
+
+ ArrayList<String> list = new ArrayList<String>();
+ for( ConstraintViolation violation : constraintViolations )
+ {
+ Locale locale;
+ if( bundle != null )
+ {
+ try
+ {
+ pattern = bundle.getString( "qi4j.constraint." + mixinTypeName + "." + methodName );
+ }
+ catch( MissingResourceException e1 )
+ {
+ try
+ {
+ pattern = bundle.getString( "qi4j.constraint" );
+ }
+ catch( MissingResourceException e2 )
+ {
+ // ignore. The default pattern will be used.
+ }
+ }
+ locale = bundle.getLocale();
+ }
+ else
+ {
+ locale = Locale.getDefault();
+ }
+ MessageFormat format = new MessageFormat( pattern, locale );
+
+ Annotation annotation = violation.constraint();
+ String name = violation.name();
+ Object value = violation.value();
+ String classes;
+ if( Iterables.count( instanceTypes ) == 1 )
+ {
+ classes = Iterables.first( instanceTypes ).getSimpleName();
+ }
+ else
+ {
+ classes = "[" + Iterables.<Class<?>>toString( instanceTypes, new Function<Class<?>, String>()
+ {
+ @Override
+ public String map( Class<?> from )
+ {
+ return from.getSimpleName();
+ }
+ }, "," ) + "]";
+ }
+ Object[] args = new Object[]
+ {
+ instanceToString,
+ classes,
+ mixinTypeName,
+ methodName,
+ annotation.toString(),
+ "" + value,
+ name
+ };
+ StringBuffer text = new StringBuffer();
+ format.format( args, text, null );
+ list.add( text.toString() );
+ }
+ String[] result = new String[ list.size() ];
+ list.toArray( result );
+ return result;
+ }
+
+ public String localizedMessage()
+ {
+ String[] messages = localizedMessagesFrom( null );
+ StringBuilder result = new StringBuilder();
+ boolean first = true;
+ for( String message : messages )
+ {
+ if( !first )
+ {
+ result.append( ',' );
+ }
+ first = false;
+ result.append( message );
+ }
+ return result.toString();
+ }
+
+ @Override
+ public String getLocalizedMessage()
+ {
+ return localizedMessage();
+ }
+
+ @Override
+ public String getMessage()
+ {
+ return localizedMessage();
+ }
+
+ public String methodName()
+ {
+ return methodName;
+ }
+
+ public String mixinTypeName()
+ {
+ return mixinTypeName;
+ }
+}
\ No newline at end of file