http://git-wip-us.apache.org/repos/asf/zest-java/blob/1c722f44/core/api/src/main/java/org/apache/polygene/api/association/ManyAssociationMixin.java ---------------------------------------------------------------------- diff --git a/core/api/src/main/java/org/apache/polygene/api/association/ManyAssociationMixin.java b/core/api/src/main/java/org/apache/polygene/api/association/ManyAssociationMixin.java new file mode 100644 index 0000000..0ed4db1 --- /dev/null +++ b/core/api/src/main/java/org/apache/polygene/api/association/ManyAssociationMixin.java @@ -0,0 +1,58 @@ +/* + * 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.association; + +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Method; +import org.apache.polygene.api.common.AppliesTo; +import org.apache.polygene.api.common.AppliesToFilter; +import org.apache.polygene.api.injection.scope.State; + +/** + * Generic mixin for associations. + */ +@AppliesTo( { ManyAssociationMixin.AssociationFilter.class } ) +public final class ManyAssociationMixin + implements InvocationHandler +{ + @State + private AssociationStateHolder associations; + + @Override + public Object invoke( Object proxy, Method method, Object[] args ) + throws Throwable + { + return associations.manyAssociationFor( method ); + } + + /** + * ManyAssociations generic mixin AppliesToFilter. + */ + static class AssociationFilter + implements AppliesToFilter + { + @Override + public boolean appliesTo( Method method, Class<?> mixin, Class<?> compositeType, Class<?> modifierClass ) + { + return ManyAssociation.class.isAssignableFrom( method.getReturnType() ); + } + } +} \ 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/association/ManyAssociationWrapper.java ---------------------------------------------------------------------- diff --git a/core/api/src/main/java/org/apache/polygene/api/association/ManyAssociationWrapper.java b/core/api/src/main/java/org/apache/polygene/api/association/ManyAssociationWrapper.java new file mode 100644 index 0000000..8465995 --- /dev/null +++ b/core/api/src/main/java/org/apache/polygene/api/association/ManyAssociationWrapper.java @@ -0,0 +1,126 @@ +/* + * 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.association; + +import java.util.Iterator; +import java.util.List; +import java.util.Set; +import java.util.stream.Stream; +import org.apache.polygene.api.entity.EntityReference; + +/** + * If you want to catch calls to ManyAssociations, then create a GenericConcern + * that wraps the Polygene-supplied ManyAssociation instance with ManyAssociationWrappers. Override + * methods to perform your custom code. + */ +public class ManyAssociationWrapper + implements ManyAssociation<Object> +{ + protected ManyAssociation<Object> next; + + public ManyAssociationWrapper( ManyAssociation<Object> next ) + { + this.next = next; + } + + public ManyAssociation<Object> next() + { + return next; + } + + @Override + public int count() + { + return next.count(); + } + + @Override + public boolean contains( Object entity ) + { + return next.contains( entity ); + } + + @Override + public boolean add( int i, Object entity ) + { + return next.add( i, entity ); + } + + @Override + public boolean add( Object entity ) + { + return next.add( entity ); + } + + @Override + public boolean remove( Object entity ) + { + return next.remove( entity ); + } + + @Override + public Object get( int i ) + { + return next.get( i ); + } + + @Override + public List<Object> toList() + { + return next.toList(); + } + + @Override + public Set<Object> toSet() + { + return next.toSet(); + } + + @Override + public Stream<EntityReference> references() + { + return next.references(); + } + + @Override + public Iterator<Object> iterator() + { + return next.iterator(); + } + + @Override + public int hashCode() + { + return next.hashCode(); + } + + @Override + @SuppressWarnings("EqualsWhichDoesntCheckParameterClass") + public boolean equals( Object obj ) + { + return next.equals( obj ); + } + + @Override + public String toString() + { + return next.toString(); + } +} http://git-wip-us.apache.org/repos/asf/zest-java/blob/1c722f44/core/api/src/main/java/org/apache/polygene/api/association/NamedAssociation.java ---------------------------------------------------------------------- diff --git a/core/api/src/main/java/org/apache/polygene/api/association/NamedAssociation.java b/core/api/src/main/java/org/apache/polygene/api/association/NamedAssociation.java new file mode 100644 index 0000000..6b5f7a6 --- /dev/null +++ b/core/api/src/main/java/org/apache/polygene/api/association/NamedAssociation.java @@ -0,0 +1,93 @@ +/* + * 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.association; + +import java.util.Map; +import java.util.stream.Stream; +import org.apache.polygene.api.entity.EntityReference; + +/** + * Association to named Entities. + * The Iterable<String> returns the names in the association set. + * @param <T> Parameterized associatee type + */ +public interface NamedAssociation<T> + extends Iterable<String>, AbstractAssociation +{ + /** + * @return The number of named associations in this NamedAssociation. + */ + int count(); + + /** + * Checks if there is an association with the given name. + * @param name The name of the association we are checking if it exists. + * @return true if it exists, false otherwise + */ + boolean containsName( String name ); + + /** + * Adds a named assocation. + * @param name The name of the association. + * @param entity The entity for this named association. + * @return true if putted, false otherwise + */ + boolean put( String name, T entity ); + + /** + * Remove a named association. + * @param name The name of the association. + * @return true if removed, false otherwise + */ + boolean remove( String name ); + + /** + * Retrieves a named association. + * @param name The name of the association. + * @return The entity that has previously been associated. + */ + T get( String name ); + + /** + * Checks if the entity is present. + * Note that this is potentially a very slow operation, depending on the size of the NamedAssociation. + * @param entity The entity to look for. + * @return The name of the entity if found, otherwise null. + */ + String nameOf( T entity ); + + /** + * @return A fully populated Map with the content of this NamedAssociation. + */ + Map<String, T> toMap(); + + /** + * Returns a stream of the references to the associated entities. + * @return the references to the associated entities. + */ + Stream<Map.Entry<String, EntityReference>> references(); + + /** Returns the EntityReference for the Association with the given name. + * + * @param name The name of the association to return the EntityReference for + * @return The EntityReference of the association. + */ + EntityReference referenceOf( String name ); +} http://git-wip-us.apache.org/repos/asf/zest-java/blob/1c722f44/core/api/src/main/java/org/apache/polygene/api/association/NamedAssociationMixin.java ---------------------------------------------------------------------- diff --git a/core/api/src/main/java/org/apache/polygene/api/association/NamedAssociationMixin.java b/core/api/src/main/java/org/apache/polygene/api/association/NamedAssociationMixin.java new file mode 100644 index 0000000..ea1ed12 --- /dev/null +++ b/core/api/src/main/java/org/apache/polygene/api/association/NamedAssociationMixin.java @@ -0,0 +1,58 @@ +/* + * 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.association; + +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Method; +import org.apache.polygene.api.common.AppliesTo; +import org.apache.polygene.api.common.AppliesToFilter; +import org.apache.polygene.api.injection.scope.State; + +/** + * Generic mixin for NamedAssociations. + */ +@AppliesTo( NamedAssociationMixin.AssociationFilter.class ) +public final class NamedAssociationMixin + implements InvocationHandler +{ + @State + private AssociationStateHolder associations; + + @Override + public Object invoke( Object proxy, Method method, Object[] args ) + throws Throwable + { + return associations.namedAssociationFor( method ); + } + + /** + * NamedAssociations generic mixin AppliesToFilter. + */ + static class AssociationFilter + implements AppliesToFilter + { + @Override + public boolean appliesTo( Method method, Class<?> mixin, Class<?> compositeType, Class<?> modifierClass ) + { + return NamedAssociation.class.isAssignableFrom( method.getReturnType() ); + } + } + +} http://git-wip-us.apache.org/repos/asf/zest-java/blob/1c722f44/core/api/src/main/java/org/apache/polygene/api/association/NamedAssociationWrapper.java ---------------------------------------------------------------------- diff --git a/core/api/src/main/java/org/apache/polygene/api/association/NamedAssociationWrapper.java b/core/api/src/main/java/org/apache/polygene/api/association/NamedAssociationWrapper.java new file mode 100644 index 0000000..4a2dcf1 --- /dev/null +++ b/core/api/src/main/java/org/apache/polygene/api/association/NamedAssociationWrapper.java @@ -0,0 +1,125 @@ +/* + * 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.association; + +import java.util.Iterator; +import java.util.Map; +import java.util.stream.Stream; +import org.apache.polygene.api.entity.EntityReference; + +/** + * If you want to catch calls to NamedAssociations, then create a GenericConcern + * that wraps the Polygene-supplied NamedAssociations instance with NamedAssociationsWrapper. Override + * methods to perform your custom code. + */ +public class NamedAssociationWrapper + implements NamedAssociation<Object> +{ + protected NamedAssociation<Object> next; + + public NamedAssociationWrapper( NamedAssociation<Object> next ) + { + this.next = next; + } + + public NamedAssociation<Object> next() + { + return next; + } + + @Override + public Iterator<String> iterator() + { + return next.iterator(); + } + + @Override + public int count() + { + return next.count(); + } + + @Override + public boolean containsName( String name ) + { + return next.containsName( name ); + } + + @Override + public boolean put( String name, Object entity ) + { + return next.put( name, entity ); + } + + @Override + public boolean remove( String name ) + { + return next.remove( name ); + } + + @Override + public Object get( String name ) + { + return next.get( name ); + } + + @Override + public String nameOf( Object entity ) + { + return next.nameOf( entity ); + } + + @Override + public Map<String, Object> toMap() + { + return next.toMap(); + } + + @Override + public Stream<Map.Entry<String, EntityReference>> references() + { + return next.references(); + } + + @Override + public EntityReference referenceOf( String name ) + { + return next.referenceOf( name ); + } + + @Override + public int hashCode() + { + return next.hashCode(); + } + + @Override + @SuppressWarnings("EqualsWhichDoesntCheckParameterClass") + public boolean equals( Object obj ) + { + return next.equals( obj ); + } + + @Override + public String toString() + { + return next.toString(); + } +} http://git-wip-us.apache.org/repos/asf/zest-java/blob/1c722f44/core/api/src/main/java/org/apache/polygene/api/association/package.html ---------------------------------------------------------------------- diff --git a/core/api/src/main/java/org/apache/polygene/api/association/package.html b/core/api/src/main/java/org/apache/polygene/api/association/package.html new file mode 100644 index 0000000..d478216 --- /dev/null +++ b/core/api/src/main/java/org/apache/polygene/api/association/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>Association 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/cache/CacheOptions.java ---------------------------------------------------------------------- diff --git a/core/api/src/main/java/org/apache/polygene/api/cache/CacheOptions.java b/core/api/src/main/java/org/apache/polygene/api/cache/CacheOptions.java new file mode 100644 index 0000000..35a0cfd --- /dev/null +++ b/core/api/src/main/java/org/apache/polygene/api/cache/CacheOptions.java @@ -0,0 +1,88 @@ +/* + * 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.cache; + +/** + * CacheOptions is a metaInfo class for the Cache system for Entity persistence. + * CacheOptions should be assigned to the Usecase of the UnitOfWork, to give hint on caching to entity stores. + * See {@link org.apache.polygene.api.usecase.UsecaseBuilder} on how to set the metaInfo on Usecases. + */ +public final class CacheOptions +{ + public static final CacheOptions ALWAYS = new CacheOptions( true, true, true ); + public static final CacheOptions NEVER = new CacheOptions( false, false, false ); + + private final boolean cacheOnRead; + private final boolean cacheOnWrite; + private final boolean cacheOnNew; + + /** + * Constructor for CacheOptions. + * + * @param cacheOnRead if true, give the hint to the Cache system that it may not be a good idea to cache the + * read values. This is useful when it is known that the read will be over a large set and + * shouldn't affect the existing cached entities. For instance, when traversing the EntityStore + * this option is set to false. + * @param cacheOnWrite if true, give the hint to the Cache system that it may not be a good idea to cache the + * entity when the value is updated. If this is false, the cache should be emptied from any + * cached entity instead of updated. There are few cases when this is useful, and if this is + * false, it makes sense that the <i>cacheOnRead</i> is also false. + * @param cacheOnNew if true, give the hint to the Cache system that it may not be a good idea to cache a newly + * created Entity, as it is not likely to be read in the near future. This is useful when + * batch inserts are being made. + */ + public CacheOptions( boolean cacheOnRead, boolean cacheOnWrite, boolean cacheOnNew ) + { + this.cacheOnRead = cacheOnRead; + this.cacheOnWrite = cacheOnWrite; + this.cacheOnNew = cacheOnNew; + } + + /** + * @return if true, give the hint to the Cache system that it may not be a good idea to cache the + * read values. This is useful when it is known that the read will be over a large set and + * shouldn't affect the existing cached entities. For instance, when traversing the EntityStore + */ + public boolean cacheOnRead() + { + return cacheOnRead; + } + + /** + * @return if true, give the hint to the Cache system that it may not be a good idea to cache the + * entity when the value is updated. If this is false, the cache should be emptied from any + * cached entity instead of updated. There are few cases when this is useful, and if this is + * false, it makes sense that the <i>cacheOnRead</i> is also false. + */ + public boolean cacheOnWrite() + { + return cacheOnWrite; + } + + /** + * @return if true, give the hint to the Cache system that it may not be a good idea to cache a newly + * created Entity, as it is not likely to be read in the near future. This is useful when + * batch inserts are being made. + */ + public boolean cacheOnNew() + { + return cacheOnNew; + } +} http://git-wip-us.apache.org/repos/asf/zest-java/blob/1c722f44/core/api/src/main/java/org/apache/polygene/api/cache/package.html ---------------------------------------------------------------------- diff --git a/core/api/src/main/java/org/apache/polygene/api/cache/package.html b/core/api/src/main/java/org/apache/polygene/api/cache/package.html new file mode 100644 index 0000000..a2109c1 --- /dev/null +++ b/core/api/src/main/java/org/apache/polygene/api/cache/package.html @@ -0,0 +1,43 @@ +<!-- + ~ 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>Cache API.</h2> + <p> + The Cache API/SPI is an extension point for Entity Store caching. + </p> + <p> + The API part is only to allow caching options to be passed to the underlying extension in a uniform and + standard way. CacheOptions are to be passed as meta info on the optional Cache extension that is specified + during assembly phase. Example; + </p> +<pre><code> +public void assemble( ModuleAssembly module ) +{ + CacheOptions options = new CacheOptions( true, true, false ); + module.addServices( EhCacheService.class ).setMetaInfo( options ); +} +</code></pre> + <p> + Not all EntityStore implementations use the Cache extension, so check the implementation details of the + EntityStore whether the cache extension can bring any benefits or not. + </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/common/AppliesTo.java ---------------------------------------------------------------------- diff --git a/core/api/src/main/java/org/apache/polygene/api/common/AppliesTo.java b/core/api/src/main/java/org/apache/polygene/api/common/AppliesTo.java new file mode 100644 index 0000000..9e88103 --- /dev/null +++ b/core/api/src/main/java/org/apache/polygene/api/common/AppliesTo.java @@ -0,0 +1,111 @@ +/* + * 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.common; + +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; + +/** + * Fragments that implement InvocationHandler and which should only be applied to methods that have a particular + * annotation or implement a known interface should use this annotation. + * <p> + * @AppliesTo can specify one of; + * </p> + * <ul> + * <li>An annotation,</li> + * <li>An interface,</li> + * <li>An AppliesToFilter implementation.</li> + * </ul> + * <p> + * Example with annotation: + * </p> + * <pre><code> + * + * @AppliesTo( Sessional.class ) // Tells Polygene to apply this concern on methods with @Sessional annotation + * public class SessionConcern extends GenericConcern + * { + * public Object invoke( Object proxy, Method method, Object[] args ) + * throws Throwable + * { + * ... do session stuff ... + * } + * } + * + * @Retention( RetentionPolicy.RUNTIME ) + * @Target( ElementType.METHOD ) + * @Documented + * @Inherited + * public @interface Sessional + * { + * } + * + * public class MyMixin + * implements My + * { + * @Sessional + * public void doSomethingSessional() + * { + * // ... do your logic wrapped in a session + * } + * + * public void doSomethingWithoutSession() + * { + * // ... do stuff that are not wrapped in session. + * } + * } + * + * public interface My + * { + * void doSomethingSessional(); + * + * void doSomethingWithoutSession(); + * } + * + * @Concerns( SessionConcern.class ) + * @Mixins( MyMixin.class ) + * public interface MyComposite extends My, TransientComposite + * {} + * </code></pre> + * <p> + * The doSomethingWithoutSession method do not have the @Sessional annotation, therefore the SessionConcern will + * not be placed into the call sequence of these methods, and + * vice-versa. The @Sessional annotation can be placed either on the interface method or the implementation + * method, depending on whether it is a contract or implementation detail. + * </p> + */ +@Retention( RetentionPolicy.RUNTIME ) +@Target( { ElementType.TYPE, ElementType.METHOD } ) +@Documented +public @interface AppliesTo +{ + /** + * List of interfaces, annotations or AppliesToFilter + * implementation classes. + * If one of them matches the current element it will be + * accepted, so this list can be considered an "or". + * + * @return array of classes or interfaces to be used by the filter + */ + Class<?>[] value(); +} http://git-wip-us.apache.org/repos/asf/zest-java/blob/1c722f44/core/api/src/main/java/org/apache/polygene/api/common/AppliesToFilter.java ---------------------------------------------------------------------- diff --git a/core/api/src/main/java/org/apache/polygene/api/common/AppliesToFilter.java b/core/api/src/main/java/org/apache/polygene/api/common/AppliesToFilter.java new file mode 100644 index 0000000..122beba --- /dev/null +++ b/core/api/src/main/java/org/apache/polygene/api/common/AppliesToFilter.java @@ -0,0 +1,89 @@ +/* + * 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.common; + +import java.lang.reflect.Method; + +/** + * Implementations of this interface can be specified in the @AppliesTo. + * <p> + * AppliesTo filters are one of the driving technologies in Polygene. They allow you to apply fragments (Mixins, + * Concerns, SideEffects), often generic ones, depending on the context that they are evaluated under. This + * mechanism is heavily used internally in Polygene to achieve many other features. + * </p> + * <p> + * The starting point is the basic use of AppliesToFilter, where the @AppliesTo annotation is given an + * AppliesToFilter implementation as an argument, for instance at a Mixin implementation; + * </p> + * <pre><code> + * @AppliesTo( MyAppliesToFilter.class ) + * public class SomeMixin + * implements InvocationHandler + * { + * + * } + * + * public class MyAppliesToFilter + * implements AppliesToFilter + * { + * public boolean appliesTo( Method method, Class<?> mixin, Class<?> compositeType, Class<?> fragmentClass ) + * { + * return method.getName().startsWith( "my" ); + * } + * } + * </code></pre> + * <p> + * In the case above, the generic mixin will only be applied to the methods that that is defined by the + * AppliesToFilter. This is the primary way to define limits on the application of generic fragments, since + * especially mixins are rarely applied to all methods. + * </p> + */ +public interface AppliesToFilter +{ + /** + * This is an internal AppliesToFilter which is assigned if no other AppliesToFilters are found for a given + * fragment. + * <p> + * There is no reason for user code to use this AppliesToFilter directly, and should be perceived as an + * internal class in Polygene. + * </p> + */ + AppliesToFilter ALWAYS = new AppliesToFilter() + { + @Override + public boolean appliesTo( Method method, Class<?> mixin, Class<?> compositeType, Class<?> fragmentClass ) + { + return true; + } + }; + + /** + * Check if the Fragment should be applied or not. Will be call when applied to Mixins, Concerns, SideEffects. + * + * @param method method that is invoked + * @param mixin mixin implementation for the method + * @param compositeType composite type + * @param fragmentClass fragment that is being applies + * + * @return true if the filter passes, otherwise false + */ + boolean appliesTo( Method method, Class<?> mixin, Class<?> compositeType, Class<?> fragmentClass ); +} http://git-wip-us.apache.org/repos/asf/zest-java/blob/1c722f44/core/api/src/main/java/org/apache/polygene/api/common/ConstructionException.java ---------------------------------------------------------------------- diff --git a/core/api/src/main/java/org/apache/polygene/api/common/ConstructionException.java b/core/api/src/main/java/org/apache/polygene/api/common/ConstructionException.java new file mode 100644 index 0000000..04a1b3a --- /dev/null +++ b/core/api/src/main/java/org/apache/polygene/api/common/ConstructionException.java @@ -0,0 +1,60 @@ +/* + * 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.common; + +/** + * Thrown when a Fragment or object could not be instantiated. + * This includes, but not be limited to; + * <ul> + * <li>private constructor.</li> + * <li>abstract class for Constraints.</li> + * <li>interface instead of a class.</li> + * <li>useful constructor missing.</li> + * <li>exception thrown in the constructor.</li> + * <li>Subclassing of org.apache.polygene.api.property.Property</li> + * </ul> + * <p> + * See the nested exception for additional details. + * </p> + */ +public class ConstructionException + extends RuntimeException +{ + private static final long serialVersionUID = 1L; + + public ConstructionException() + { + } + + public ConstructionException( String message ) + { + super( message ); + } + + public ConstructionException( String message, Throwable cause ) + { + super( message, cause ); + } + + public ConstructionException( Throwable cause ) + { + super( cause ); + } +} http://git-wip-us.apache.org/repos/asf/zest-java/blob/1c722f44/core/api/src/main/java/org/apache/polygene/api/common/InvalidApplicationException.java ---------------------------------------------------------------------- diff --git a/core/api/src/main/java/org/apache/polygene/api/common/InvalidApplicationException.java b/core/api/src/main/java/org/apache/polygene/api/common/InvalidApplicationException.java new file mode 100644 index 0000000..fccd4bb --- /dev/null +++ b/core/api/src/main/java/org/apache/polygene/api/common/InvalidApplicationException.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.common; + +/** + * Thrown when an application is considered to not be constructed properly. + * This happens primarily when client code tries to instantiate Composites + * and objects which have not been registered in the ModuleAssembly. + */ +public class InvalidApplicationException + extends RuntimeException +{ + private static final long serialVersionUID = 1L; + + public InvalidApplicationException( String string ) + { + super( string ); + } + + public InvalidApplicationException( String string, Throwable cause ) + { + super( string, cause ); + } +} http://git-wip-us.apache.org/repos/asf/zest-java/blob/1c722f44/core/api/src/main/java/org/apache/polygene/api/common/MetaInfo.java ---------------------------------------------------------------------- diff --git a/core/api/src/main/java/org/apache/polygene/api/common/MetaInfo.java b/core/api/src/main/java/org/apache/polygene/api/common/MetaInfo.java new file mode 100644 index 0000000..c3f3a11 --- /dev/null +++ b/core/api/src/main/java/org/apache/polygene/api/common/MetaInfo.java @@ -0,0 +1,154 @@ +/* + * 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.common; + +import java.lang.annotation.Annotation; +import java.lang.reflect.AnnotatedElement; +import java.util.Collection; +import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.Map; +import org.apache.polygene.api.concern.Concerns; +import org.apache.polygene.api.mixin.Mixins; +import org.apache.polygene.api.sideeffect.SideEffects; +import org.apache.polygene.api.util.Classes; + +import static java.util.Arrays.asList; +import static org.apache.polygene.api.util.Classes.typesOf; + +/** + * Used to declare and access meta-info. + * <p> + * <strong>This is effectively an internal class and should not be used directly.</strong> + * </p> + * <p> + * MetaInfo can be set on composites during the assembly phase, a.k.a the bootstrap + * process. MetaInfo is any additional data that one wishes to associate at the 'class level' instead of instance + * level of a composite declaration. + * </p> + * <p> + * To set the MetaInfo on a Composite, call the {@code setMetaInfo()} methods on the various composite declaration + * types, such as; + * </p> + * <pre><code> + * public void assemble( ModuleAssembly module ) + * throws AssemblyException + * { + * Map<String,String> properties = ...; + * module.services( MyService.class ).setMetaInfo( properties ); + * } + * </code></pre> + * <p> + * which can later be retrieved by calling the {@code metaInfo()} method on the composite itself. For the example + * above that would be; + * </p> + * <pre><code> + * @Mixins(MyServiceMixin.class) + * public interface MyService extends ServiceComposite + * { + * + * } + * + * public abstract class MyServiceMixin + * implements MyService + * { + * private Properties props; + * + * public MyServiceMixin() + * { + * props = metaInfo( Map.class ); + * } + * } + * </code></pre> + */ +public final class MetaInfo +{ + private final static Collection<Class> ignored; + + static + { + ignored = new HashSet<>( 4, 0.8f ); // Optimize size used. + ignored.addAll( asList( Mixins.class, Concerns.class, SideEffects.class ) ); + } + + private final Map<Class<?>, Object> metaInfoMap; + + public MetaInfo() + { + metaInfoMap = new LinkedHashMap<>(); + } + + public MetaInfo( MetaInfo metaInfo ) + { + metaInfoMap = new LinkedHashMap<>(); + metaInfoMap.putAll( metaInfo.metaInfoMap ); + } + + public void set( Object metaInfo ) + { + if( metaInfo instanceof Annotation ) + { + Annotation annotation = (Annotation) metaInfo; + metaInfoMap.put( annotation.annotationType(), metaInfo ); + } + else + { + Class<?> metaInfoclass = metaInfo.getClass(); + typesOf( metaInfoclass ) + .map( Classes.RAW_CLASS ) + .forEach( type -> metaInfoMap.put( type, metaInfo ) ); + } + } + + public <T> T get( Class<T> metaInfoType ) + { + return metaInfoType.cast( metaInfoMap.get( metaInfoType ) ); + } + + public <T> void add( Class<T> infoType, T info ) + { + metaInfoMap.put( infoType, info ); + } + + public MetaInfo withAnnotations( AnnotatedElement annotatedElement ) + { + for( Annotation annotation : annotatedElement.getAnnotations() ) + { + if( !ignored.contains( annotation.annotationType() ) + && get( annotation.annotationType() ) == null ) + { + set( annotation ); + } + } + return this; + } + + @Override + public String toString() + { + return metaInfoMap.toString(); + } + + public void remove( Class serviceFinderClass ) + { + metaInfoMap.remove( serviceFinderClass ); + } +} http://git-wip-us.apache.org/repos/asf/zest-java/blob/1c722f44/core/api/src/main/java/org/apache/polygene/api/common/Optional.java ---------------------------------------------------------------------- diff --git a/core/api/src/main/java/org/apache/polygene/api/common/Optional.java b/core/api/src/main/java/org/apache/polygene/api/common/Optional.java new file mode 100644 index 0000000..049417e --- /dev/null +++ b/core/api/src/main/java/org/apache/polygene/api/common/Optional.java @@ -0,0 +1,68 @@ +/* + * 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.common; + +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; + +/** + * Annotation to denote that something is optional. + * <ul> + * <li> + * If applied to a method parameter, then the value is allowed to be null. Default + * is that method parameters have to be non-null. + * </li> + * <li> + * If applied to a Property declaration, then the value may be null after construction of + * the instance, or may be set to null at a later time. + * </li> + * <li> + * If applied to an injected member field, it is allowed tha none get injected. For instance, an <code>@Optional + * @Service</code> would allow a service to not have been declared and the field will be null. + * </li> + * </ul> + * <p> + * Optionality is not the default in Polygene, and if injections, property values and parameters in methods are not + * non-null, the Polygene runtime will throw an {@link org.apache.polygene.api.constraint.ConstraintViolationException}, indicating + * which field/property/parameter in which composite and mixin the problem has been detected. + * </p> + * <p> + * Example; + * </p> + * <pre><code> + * @Optional @Service + * MyService service; // If no MyService instance is declared and visible to this service injection point + * // the 'service' field will be null. + * + * @Service + * YourService other; // If no YourService instance is declared and visible to this service injection point + * // the Polygene runtime will throw a ConstraintViolationException. + * + * </code></pre> + */ +@Retention( RetentionPolicy.RUNTIME ) +@Target( { ElementType.PARAMETER, ElementType.METHOD, ElementType.FIELD } ) +@Documented +public @interface Optional +{ +} \ 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/common/QualifiedName.java ---------------------------------------------------------------------- diff --git a/core/api/src/main/java/org/apache/polygene/api/common/QualifiedName.java b/core/api/src/main/java/org/apache/polygene/api/common/QualifiedName.java new file mode 100644 index 0000000..b0d0f18 --- /dev/null +++ b/core/api/src/main/java/org/apache/polygene/api/common/QualifiedName.java @@ -0,0 +1,258 @@ +/* + * 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.common; + +import java.io.Serializable; +import java.lang.reflect.AccessibleObject; +import java.lang.reflect.Member; +import org.apache.polygene.api.util.NullArgumentException; + +/** + * QualifiedName is a representation of Property names to their full declaration. + * <p> + * A QualifiedName is created by combining the name of a method and the name of the type that declares the method. + * This class also contains many static utility methods to manage QualifiedName instances. + * </p> + * <p> + * <strong>NOTE: Unless you do very generic libraries, entity stores and other extensions that is deeply coupled into + * the Polygene runtime, it is very unlikely you will need to use this class directly.</strong> + * </p> + * <p> + * It is also important to notice that the QualifiedName needs to be long-term stable, as the names are written + * to persistent storage. So any changes in the formatting <strong>must be made in a backward-compatible manner + * </strong>. + * </p> + * <p> + * The QualifiedName has two intrinsic parts, one being the {@code type} and the other the {@code name}. The + * {@code type} comes from the class where the QualifiedName originates from and internally kept as a {@link TypeName} + * instance. The name is the name from the method name. When the QualifiedName instance is converted to an external + * string representation, via the offical and formal {@link #toString()} method, the {@code type} is normalized, i.e. + * any dollar characters ($) in the name are replaced by dashes (-), to make them URI friendly. + * </p> + * <p> + * QualifiedName instances are immutable, implements {@link #hashCode()} and {@link #equals(Object)} as a value + * object and can safely be used as keys in {@link java.util.Map}. + */ +public final class QualifiedName + implements Comparable<QualifiedName>, Serializable +{ + private final TypeName typeName; + private final String name; + + /** + * Creates a QualifiedName from a method. + * <p> + * This factory method will create a QualifiedName from the Method itself. + * + * </p> + * + * @param method Type method that returns a Property, for which the QualifiedName will be representing. + * + * @return A QualifiedName representing this method. + * + * @throws NullArgumentException If the {@code method} argument passed is null. + */ + public static QualifiedName fromAccessor( AccessibleObject method ) + { + NullArgumentException.validateNotNull( "method", method ); + return fromClass( ( (Member) method ).getDeclaringClass(), ( (Member) method ).getName() ); + } + + /** + * Creates a QualifiedName instance from the Class and a given name. + * <p> + * This factory method converts the {@code type} to a {@link TypeName} and appends the given {@code name}. + * + * @param type The Class that is the base of the QualifiedName. + * @param name The qualifier name which will be appended to the base name derived from the {@code type} argument. + * + * @return A QualifiedName instance representing the {@code type} and {@code name} arguments. + * + * @throws NullArgumentException if any of the two arguments are {@code null}, or if the name string is empty. + */ + public static QualifiedName fromClass( Class type, String name ) + { + return new QualifiedName( TypeName.nameOf( type ), name ); + } + + /** + * Creates a Qualified name from a type as string and a name qualifier. + * + * @param type The type name as a a string, which must be properly formatted. No checks for correctly formatted + * type name is performed. + * @param name The qualifier name which will be appended to the base name derived from the {@code type} argument. + * + * @return A QualifiedName instance representing the {@code type} and {@code name} arguments. + * + * @throws NullArgumentException if any of the two arguments are {@code null} or either string is empty. + */ + public static QualifiedName fromName( String type, String name ) + { + return new QualifiedName( TypeName.nameOf( type ), name ); + } + + /** + * Creates a QualifiedName from the external string format of QualifiedName. + * <p> + * This factory method is the reverse of {@link QualifiedName#toString() } method, and creates a new QualifiedName + * instance from the string representation of the QualifiedName. + * </p> + * + * @param fullQualifiedName The QualifiedName external string representation to be converted back into a QualifiedName + * instance. + * + * @return The QualifiedName instance represented by the {@code qualifiedName} argument. + * + * @throws IllegalArgumentException If the {@code qualifiedName} argument has wrong format. + */ + public static QualifiedName fromFQN( String fullQualifiedName ) + { + NullArgumentException.validateNotEmpty( "qualifiedName", fullQualifiedName ); + int idx = fullQualifiedName.lastIndexOf( ":" ); + if( idx == -1 ) + { + throw new IllegalArgumentException( "Name '" + fullQualifiedName + "' is not a qualified name" ); + } + final String type = fullQualifiedName.substring( 0, idx ); + final String name = fullQualifiedName.substring( idx + 1 ); + return new QualifiedName( TypeName.nameOf( type ), name ); + } + + QualifiedName( TypeName typeName, String name ) + { + NullArgumentException.validateNotNull( "typeName", typeName ); + NullArgumentException.validateNotEmpty( "name", name ); + this.typeName = typeName; + this.name = name; + } + + /** + * Returns the normalized string of the type part of the QualifiedName. + * + * <p> + * The normalized type name means that all dollar ($) characters have been replaced by dashes (-). + * </p> + * + * @return the normalized string of the type part of the QualifiedName. + */ + public String type() + { + return typeName.normalized(); + } + + /** + * Returns the name component of the QualifiedName. + * + * @return the name component of the QualifiedName. + */ + public String name() + { + return name; + } + + /** + * Returns the URI of the QualifiedName. + * + * <p> + * The URI is the {@link #toNamespace()} followed by the {@code name} component. + * <p> + * + * @return the URI of the QualifiedName. + * + * @see #toNamespace() + */ + public String toURI() + { + return toNamespace() + name; + } + + /** + * Return the URI of the {@link TypeName} component of the QualifiedName. + * <p> + * The URI of the {@link TypeName} component is in the form of; + * </p> + * <pre> + * "urn:polygene:type:" normalizedClassName + * </pre> + * <p> + * where {@code normalizedClassName} is the fully-qualified class name having had any dollar ($) characters replaced + * by URI friendly dashes (-), with a trailing hash (#). Examples; + * </p> + * <pre> + * urn:polygene:type:org.apache.polygene.api.common.QualifiedName# + * urn:polygene:type:org.apache.polygene.samples.MyClass-MyInnerClass# + * </pre> + * + * @return the URI of the {@link TypeName} component of the QualifiedName. + */ + public String toNamespace() + { + return typeName.toURI() + "#"; + } + + /** + * Return the formal and official, long-term stable, external string representation of a QualifiedName. + * <p> + * This returns the {@link org.apache.polygene.api.common.TypeName#toString()} followed by the {@code name} component. + * </p> + * + * @return the formal and official, long-term stable, external string representation of a QualifiedName. + */ + @Override + public String toString() + { + return typeName + ":" + name; + } + + @Override + public boolean equals( Object o ) + { + if( this == o ) + { + return true; + } + if( o == null || getClass() != o.getClass() ) + { + return false; + } + + QualifiedName that = (QualifiedName) o; + + return name.equals( that.name ) && typeName.equals( that.typeName ); + } + + @Override + public int hashCode() + { + return 31 * typeName.hashCode() + name.hashCode(); + } + + @Override + public int compareTo( QualifiedName other ) + { + final int result = typeName.compareTo( other.typeName ); + if( result != 0 ) + { + return result; + } + return name.compareTo( other.name ); + } +} http://git-wip-us.apache.org/repos/asf/zest-java/blob/1c722f44/core/api/src/main/java/org/apache/polygene/api/common/TypeName.java ---------------------------------------------------------------------- diff --git a/core/api/src/main/java/org/apache/polygene/api/common/TypeName.java b/core/api/src/main/java/org/apache/polygene/api/common/TypeName.java new file mode 100644 index 0000000..8ee64c8 --- /dev/null +++ b/core/api/src/main/java/org/apache/polygene/api/common/TypeName.java @@ -0,0 +1,112 @@ +/* + * 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.common; + +import java.io.Serializable; +import java.lang.reflect.Type; +import org.apache.polygene.api.util.Classes; +import org.apache.polygene.api.util.NullArgumentException; + +/** + * Represents a Type name. + */ +public final class TypeName + implements Serializable, Comparable<TypeName> +{ + private final String name; + + public static TypeName nameOf( Class type ) + { + NullArgumentException.validateNotNull( "type", type ); + return new TypeName( type.getName() ); + } + + public static TypeName nameOf( Type type ) + { + return nameOf( Classes.RAW_CLASS.apply( type ) ); + } + + public static TypeName nameOf( String typeName ) + { + return new TypeName( typeName ); + } + + private TypeName( String name ) + { + NullArgumentException.validateNotEmpty( "name", name ); + this.name = name; + } + + public String normalized() + { + return Classes.normalizeClassToURI( name ); + } + + public String toURI() + { + return Classes.toURI( name ); + } + + public String name() + { + return name; + } + + @Override + public String toString() + { + return name; + } + + public boolean isClass( final Class<?> type ) + { + return type.getName().equals( name ); + } + + @Override + public boolean equals( final Object o ) + { + if( this == o ) + { + return true; + } + if( o == null || getClass() != o.getClass() ) + { + return false; + } + + final TypeName other = (TypeName) o; + + return name.equals( other.name ); + } + + @Override + public int hashCode() + { + return name.hashCode(); + } + + @Override + public int compareTo( final TypeName typeName ) + { + return this.name.compareTo( typeName.name ); + } +} + http://git-wip-us.apache.org/repos/asf/zest-java/blob/1c722f44/core/api/src/main/java/org/apache/polygene/api/common/UseDefaults.java ---------------------------------------------------------------------- diff --git a/core/api/src/main/java/org/apache/polygene/api/common/UseDefaults.java b/core/api/src/main/java/org/apache/polygene/api/common/UseDefaults.java new file mode 100644 index 0000000..8876ae7 --- /dev/null +++ b/core/api/src/main/java/org/apache/polygene/api/common/UseDefaults.java @@ -0,0 +1,92 @@ +/* + * 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.common; + +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; + +/** + * Annotation to denote that the initial value of a Property will be the default value for the type if none is + * specified during construction. + * <p> + * These are the default values used for various types: + * </p> + * <pre> + * Byte: 0 + * Short: 0 + * Character: 0 + * Integer: 0 + * Long: 0L + * Double: 0.0d + * Float: 0.0f + * Boolean: false + * String: "" + * List: empty java.util.ArrayList + * Set: empty java.util.HashSet + * Collection: empty java.util.ArrayList + * enum: first declared value + * </pre> + * <p> + * If this annotation is not used, the property will be set to null, and unless {@code @Optional} is declared + * is not allowed. + * </p> + * <p> + * The <code>@UseDefaults</code> annotation can also have a value in its declaration. This value is used, + * unless it is overridden in the assembly (see below). Java does not support generic types of annotation values, + * so it accepts String values, which are deserialized from JSON using the ValueSerialization SPI. This allows + * for (albeit somewhat tedious) any object type to have a default value declared on it. If the property type is + * String, then no value deserialization is done. + * </p> + * <p> + * It is also possible to change the default values for Composites during the assembly. This is done by calling the + * {@code org.apache.polygene.bootstrap.ModuleAssembly#forMixin(Class)} method. + * </p> + * <p> + * Example; + * Let's assume that we have the following mixin type; + * + * <pre><code> + * public interface SomeType + * { + * @UseDefaults + * Property<String> someValue(); + * } + * </code></pre> + * And that we want to have {@code someValue()} to be initialized to "<unknown>" instead of the empty string. + * Then we need to declare the default for that with the following in the assembler. + * <pre><code> + * public void assemble( ModuleAssembly module ) + * { + * module.forMixin( SomeType.class ).declareDefaults().someValue().set( "<unknown>" ); + * } + * } + * </code></pre> + */ +@SuppressWarnings( "JavadocReference" ) +@Retention( RetentionPolicy.RUNTIME ) +@Target( { ElementType.METHOD, ElementType.FIELD } ) +@Documented +public @interface UseDefaults +{ + String value() default ""; +} \ 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/common/Visibility.java ---------------------------------------------------------------------- diff --git a/core/api/src/main/java/org/apache/polygene/api/common/Visibility.java b/core/api/src/main/java/org/apache/polygene/api/common/Visibility.java new file mode 100644 index 0000000..6120143 --- /dev/null +++ b/core/api/src/main/java/org/apache/polygene/api/common/Visibility.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.common; + +/** + * Visibility is a core concept in the Polygene structure system. It defines the locale of composites and objects, i.e. + * how far they can be 'seen' and therefor be used. + * <p> + * When a Composite or Object is declared in the assembly phase, and no visibility is set, only other + * composites/objects within the same module can use that declaration. For a declared composite/object to be usable + * from other modules a higher visibility must be set, either that the Composite/Object can be used by others within + * the same Layer, or even to be used by those in the layer above. + * </p> + */ +public enum Visibility +{ + /** + * Artifact is visible only in the declaring module (default) + */ + module, + /** + * Artifact is visible to all modules in the same layer + */ + layer, + /** + * Artifact is visible to other modules in the same layer and any modules in extending layers + */ + application +} http://git-wip-us.apache.org/repos/asf/zest-java/blob/1c722f44/core/api/src/main/java/org/apache/polygene/api/common/package.html ---------------------------------------------------------------------- diff --git a/core/api/src/main/java/org/apache/polygene/api/common/package.html b/core/api/src/main/java/org/apache/polygene/api/common/package.html new file mode 100644 index 0000000..38a7a39 --- /dev/null +++ b/core/api/src/main/java/org/apache/polygene/api/common/package.html @@ -0,0 +1,84 @@ +<!-- + ~ 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>Common API.</h2> + <p> + The Common API package is a collection of really low-level types needed at the core of the Polygene⢠Runtime. It is also + a collection of types that are not particularly cohesive, and effectively this package contains the loose ends + that does not belong elsewhere. + </p> + <p> + In this package, you can safely ignore the following classes; + </p> + <ul> + <li>MetaInfo</li> + <li>QualifiedName</li> + <li>TypeName</li> + </ul> + <p>UNLESS you are into deep integration into the Polygene⢠Runtime.</p> + + <h3>@AppliesTo and AppliesToFilter</h3> + <p> + This tandem of interface + annotation are primarily used for Generic Fragments, to indicate which methods on the + interface the fragment should be applied to. + </p> + + <h3>@Optional</h3> + <p> + In Polygeneâ¢, method arguments, property values and injected fields are not allowed to be null by default. To allow + any of these to be null, i.e. undeclared, it is required that the argument, field or method is marked with the + @Optional annotation. + </p> + + <h3>@UseDefaults</h3> + <p> + Since null is not allowed without the @Optional annotation, it can sometimes by tedious to initialize all + the property values. And the @UseDefaults annotation allows us to declare that Polygene⢠should set the Property + to a default value. These are either the pre-defined ones, or can be set per property declaration during the + assembly. + </p> + + <h3>@Visibility</h3> + <p> + Visibility is another innovative concept in Polygeneâ¢, which leverage the structure system (Application, Layer, Module) + to limit the 'reach' when requesting composites and objects. The Visibility is declared per Composite/Object, + preferably in the most restrictive mode possible, and the visibility resolver will ensure a predictable resolution + algorithm; + </p> + <ol> + <li>Search the module of the caller first. If one and only one composite type fulfilling the request is available + return that to the caller. If two or more are found, throw an AmbiguousTypeException. If no composite found + continue to the next step. + </li> + <li>Search all modules in the Layer of the caller for composite that has a declaration other than + <code>Visibility.module</code>. If one and only one composite type fulfilling the request is available + return that to the caller. If two or more are found, throw an AmbiguousTypeException. If no composite found + continue to the next step. + </li> + <li>Search all modules in the Layer(s) (if any) directly below of the caller for composite that has a declaration of + <code>Visibility.application</code>. If one and only one composite type fulfilling the request is available + return that to the caller. If two or more are found, throw an AmbiguousTypeException. If no composite found + continue to the next step. + </li> + <li>Throw an NoSuchCompositeException (or related) exception.</li> + </ol> + </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/composite/AmbiguousTypeException.java ---------------------------------------------------------------------- diff --git a/core/api/src/main/java/org/apache/polygene/api/composite/AmbiguousTypeException.java b/core/api/src/main/java/org/apache/polygene/api/composite/AmbiguousTypeException.java new file mode 100644 index 0000000..8c96697 --- /dev/null +++ b/core/api/src/main/java/org/apache/polygene/api/composite/AmbiguousTypeException.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; + +/** + * This Exception is thrown when more than one Composite implements a MixinType + * that one tries to use to create a Composite instance from. + * <p> + * For instance; + * </p> + * <pre><code> + * public interface AbcComposite extends TransientComposite, Abc + * {} + * + * public interface DefComposite extends TransientComposite, Def + * {} + * + * public interface Abc + * {} + * + * public interface Def extends Abc + * {} + * + * + * TransientBuilder cb = factory.newTransientBuilder( Abc.class ); + * </code></pre> + * <p> + * In the code above, both the AbcComposite and DefComposite implement Abc, and therefore + * the <code>newTransientBuilder</code> method can not unambiguously figure out which + * one is intended. + * </p> + */ +public class AmbiguousTypeException + extends RuntimeException +{ + private static final long serialVersionUID = 1L; + + public AmbiguousTypeException( 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/Composite.java ---------------------------------------------------------------------- diff --git a/core/api/src/main/java/org/apache/polygene/api/composite/Composite.java b/core/api/src/main/java/org/apache/polygene/api/composite/Composite.java new file mode 100644 index 0000000..42f5e77 --- /dev/null +++ b/core/api/src/main/java/org/apache/polygene/api/composite/Composite.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 org.apache.polygene.api.mixin.Mixins; +import org.apache.polygene.api.property.PropertyMixin; + +/** + * Base Composite interface. + * <p> + * All Composite objects must implement this interface. Let the + * Composite interface extend this one. An implementation will be provided + * by the framework. + * </p> + * <p> + * Properties and associations are handled by default. + * </p> + */ +@Mixins( { PropertyMixin.class } ) +public interface Composite +{ +} http://git-wip-us.apache.org/repos/asf/zest-java/blob/1c722f44/core/api/src/main/java/org/apache/polygene/api/composite/CompositeContext.java ---------------------------------------------------------------------- diff --git a/core/api/src/main/java/org/apache/polygene/api/composite/CompositeContext.java b/core/api/src/main/java/org/apache/polygene/api/composite/CompositeContext.java new file mode 100644 index 0000000..6c32c81 --- /dev/null +++ b/core/api/src/main/java/org/apache/polygene/api/composite/CompositeContext.java @@ -0,0 +1,84 @@ +/* + * 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.InvocationHandler; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; +import java.util.stream.Stream; +import org.apache.polygene.api.PolygeneAPI; +import org.apache.polygene.api.structure.ModuleDescriptor; + +/** + * Thread-associated composites. This is basically a ThreadLocal which maintains a reference + * to a TransientComposite instance for each thread. This can be used to implement various context + * patterns without having to pass the context explicitly as a parameter to methods. + */ +public class CompositeContext<T extends TransientComposite> + extends ThreadLocal<T> +{ + private ModuleDescriptor module; + private Class<T> type; + + public CompositeContext( ModuleDescriptor module, Class<T> type ) + { + this.module = module; + this.type = type; + } + + @Override + protected T initialValue() + { + return module.instance().newTransient( type ); + } + + @SuppressWarnings( "unchecked" ) + public T proxy() + { + TransientComposite composite = get(); + + Stream<Class<?>> types = PolygeneAPI.FUNCTION_COMPOSITE_INSTANCE_OF.apply( composite ).types(); + return (T) Proxy.newProxyInstance( + composite.getClass().getClassLoader(), + types.toArray( Class[]::new ), + new ContextInvocationhandler() ); + } + + private class ContextInvocationhandler + implements InvocationHandler + { + + @Override + public Object invoke( Object object, Method method, Object[] objects ) + throws Throwable + { + try + { + return method.invoke( get(), objects ); + } + catch( InvocationTargetException e ) + { + throw e.getTargetException(); + } + } + } +} http://git-wip-us.apache.org/repos/asf/zest-java/blob/1c722f44/core/api/src/main/java/org/apache/polygene/api/composite/CompositeDescriptor.java ---------------------------------------------------------------------- diff --git a/core/api/src/main/java/org/apache/polygene/api/composite/CompositeDescriptor.java b/core/api/src/main/java/org/apache/polygene/api/composite/CompositeDescriptor.java new file mode 100644 index 0000000..bd5d65e --- /dev/null +++ b/core/api/src/main/java/org/apache/polygene/api/composite/CompositeDescriptor.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.composite; + +import java.util.stream.Stream; + +/** + * Composite Descriptor. + */ +public interface CompositeDescriptor + extends ModelDescriptor +{ + Class<?> primaryType(); + + Stream<Class<?>> mixinTypes(); +} http://git-wip-us.apache.org/repos/asf/zest-java/blob/1c722f44/core/api/src/main/java/org/apache/polygene/api/composite/CompositeInstance.java ---------------------------------------------------------------------- diff --git a/core/api/src/main/java/org/apache/polygene/api/composite/CompositeInstance.java b/core/api/src/main/java/org/apache/polygene/api/composite/CompositeInstance.java new file mode 100644 index 0000000..03e6dc8 --- /dev/null +++ b/core/api/src/main/java/org/apache/polygene/api/composite/CompositeInstance.java @@ -0,0 +1,45 @@ +/* + * 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.InvocationHandler; +import org.apache.polygene.api.property.StateHolder; +import org.apache.polygene.api.structure.MetaInfoHolder; +import org.apache.polygene.api.structure.ModuleDescriptor; +import org.apache.polygene.api.type.HasTypes; + +/** + * Composite Instance. + */ +public interface CompositeInstance + extends InvocationHandler, CompositeInvoker, HasTypes, MetaInfoHolder +{ + <T> T proxy(); + + <T> T newProxy( Class<T> mixinType ) + throws IllegalArgumentException; + + ModuleDescriptor module(); + + CompositeDescriptor descriptor(); + + StateHolder state(); +} http://git-wip-us.apache.org/repos/asf/zest-java/blob/1c722f44/core/api/src/main/java/org/apache/polygene/api/composite/CompositeInvoker.java ---------------------------------------------------------------------- diff --git a/core/api/src/main/java/org/apache/polygene/api/composite/CompositeInvoker.java b/core/api/src/main/java/org/apache/polygene/api/composite/CompositeInvoker.java new file mode 100644 index 0000000..7cf6fe5 --- /dev/null +++ b/core/api/src/main/java/org/apache/polygene/api/composite/CompositeInvoker.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.reflect.Method; + +/** + * Composite method invoker. + * <p> + * All composites must implement this interface. Methods that are invoked + * may reside either in the public Composite interface or in any internal mixins. + * </p> + * <p> + * <strong><i>NOTE:</i></strong>Client code should never use method in this class. We have not been able to hide this + * from client code, but IF we find a way to do, this interface may disappear. + * </p> + */ +public interface CompositeInvoker +{ + + Object invokeComposite( Method method, Object[] args ) + throws Throwable; + +} http://git-wip-us.apache.org/repos/asf/zest-java/blob/1c722f44/core/api/src/main/java/org/apache/polygene/api/composite/ConstructorDescriptor.java ---------------------------------------------------------------------- diff --git a/core/api/src/main/java/org/apache/polygene/api/composite/ConstructorDescriptor.java b/core/api/src/main/java/org/apache/polygene/api/composite/ConstructorDescriptor.java new file mode 100644 index 0000000..dd09eae --- /dev/null +++ b/core/api/src/main/java/org/apache/polygene/api/composite/ConstructorDescriptor.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.Constructor; + +/** + * Composite constructor descriptor. + */ +public interface ConstructorDescriptor +{ + Constructor<?> constructor(); +} http://git-wip-us.apache.org/repos/asf/zest-java/blob/1c722f44/core/api/src/main/java/org/apache/polygene/api/composite/DecoratorMixin.java ---------------------------------------------------------------------- diff --git a/core/api/src/main/java/org/apache/polygene/api/composite/DecoratorMixin.java b/core/api/src/main/java/org/apache/polygene/api/composite/DecoratorMixin.java new file mode 100644 index 0000000..1707f3e --- /dev/null +++ b/core/api/src/main/java/org/apache/polygene/api/composite/DecoratorMixin.java @@ -0,0 +1,106 @@ +/* + * 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.InvocationHandler; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; +import org.apache.polygene.api.injection.scope.Uses; + +/** + * Generic decorator mixin that allows a Composite to wrap + * any other Composite as long as they share an interface. + * <p> + * Can be used to effectively implement + * singleton mixins, since the decorated object can be shared between + * many instances. + * </p> + */ +public class DecoratorMixin + implements InvocationHandler +{ + private Object delegate; + + public DecoratorMixin( @Uses Object delegate ) + { + if( delegate instanceof Class ) + { + Thread.dumpStack(); + } + this.delegate = delegate; + } + + @Override + public Object invoke( Object object, Method method, Object[] args ) + throws Throwable + { + if( delegate instanceof InvocationHandler ) + { + InvocationHandler handler = (InvocationHandler) delegate; + return handler.invoke( object, method, args ); + } + else + { + try + { + return method.invoke( delegate, args ); + } + catch( InvocationTargetException e ) + { + throw e.getCause(); + } + catch( IllegalArgumentException e ) + { + String message = constructMessage( method, args ); + throw new IllegalArgumentException( message, e ); + } + } + } + + private String constructMessage( Method method, Object[] args ) + { + StringBuilder builder = new StringBuilder(); + builder.append( "\nmethod: " ); + builder.append( method.getDeclaringClass().getName() ); + builder.append( "." ); + builder.append( method.getName() ); + builder.append( "\ndelegate: " ); + builder.append( delegate ); + builder.append( "\ndelegateType: " ); + builder.append( delegate == null ? "n/a" : delegate.getClass().getName() ); + builder.append( "\narguments: \n" ); + for( Object arg : args ) + { + builder.append( " " ); + Class argClass = arg.getClass(); + if( Proxy.isProxyClass( argClass ) ) + { + builder.append( Proxy.getInvocationHandler( arg ).getClass().getName() ); + } + else + { + builder.append( argClass.getName() ); + } + builder.append( '\n' ); + } + return builder.toString(); + } +}
