/* * Copyright 2015 Red Hat, Inc. and/or its affiliates. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * * 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.drools.compiler.builder.impl; import java.io.IOException; import java.lang.reflect.InvocationTargetException; import java.util.Map; import org.drools.compiler.compiler.BoundIdentifiers; import org.drools.compiler.compiler.PackageRegistry; import org.drools.compiler.compiler.TypeDeclarationError; import org.drools.compiler.lang.descr.AbstractClassTypeDeclarationDescr; import org.drools.compiler.lang.descr.Annotated; import org.drools.compiler.lang.descr.BaseDescr; import org.drools.compiler.rule.builder.PackageBuildContext; import org.drools.compiler.rule.builder.dialect.mvel.MVELAnalysisResult; import org.drools.compiler.rule.builder.dialect.mvel.MVELDialect; import org.drools.core.base.ClassFieldAccessor; import org.drools.core.base.ClassFieldAccessorStore; import org.drools.core.base.evaluators.TimeIntervalParser; import org.drools.core.base.mvel.MVELCompileable; import org.drools.core.definitions.InternalKnowledgePackage; import org.drools.core.factmodel.ClassDefinition; import org.drools.core.factmodel.FieldDefinition; import org.drools.core.rule.MVELDialectRuntimeData; import org.drools.core.rule.TypeDeclaration; import org.drools.core.spi.InternalReadAccessor; import org.drools.core.util.ClassUtils; import org.kie.api.definition.type.ClassReactive; import org.kie.api.definition.type.Duration; import org.kie.api.definition.type.Expires; import org.kie.api.definition.type.PropertyReactive; import org.kie.api.definition.type.Role; import org.kie.api.definition.type.Timestamp; import org.kie.internal.builder.conf.PropertySpecificOption; public class TypeDeclarationConfigurator { protected KnowledgeBuilderImpl kbuilder; public TypeDeclarationConfigurator( KnowledgeBuilderImpl kbuilder ) { this.kbuilder = kbuilder; } public void finalize( TypeDeclaration type, AbstractClassTypeDeclarationDescr typeDescr, PackageRegistry pkgRegistry, Map<String, PackageRegistry> pkgRegistryMap, ClassHierarchyManager hierarchyManager ) { // prefer definitions where possible if ( type.getNature() == TypeDeclaration.Nature.DEFINITION ) { hierarchyManager.addDeclarationToPackagePreservingOrder( type, typeDescr, pkgRegistry.getPackage(), pkgRegistryMap ); } else { TypeDeclaration oldType = pkgRegistry.getPackage().getTypeDeclaration( type.getTypeName() ); if ( oldType == null ) { pkgRegistry.getPackage().addTypeDeclaration( type ); } else { if (type.getRole() == Role.Type.EVENT) { oldType.setRole(Role.Type.EVENT); if ( type.getDurationAttribute() != null ) { oldType.setDurationAttribute( type.getDurationAttribute() ); oldType.setDurationExtractor( type.getDurationExtractor() ); } if ( type.getTimestampAttribute() != null ) { oldType.setTimestampAttribute( type.getTimestampAttribute() ); oldType.setTimestampExtractor( type.getTimestampExtractor() ); } if ( type.getExpirationOffset() >= 0 ) { oldType.setExpirationOffset( type.getExpirationOffset() ); oldType.setExpirationType( type.getExpirationPolicy() ); } } if (type.isPropertyReactive()) { oldType.setPropertyReactive(true); } } } } public boolean wireFieldAccessors( PackageRegistry pkgRegistry, AbstractClassTypeDeclarationDescr typeDescr, TypeDeclaration type ) { if ( type.getTypeClassDef() != null ) { try { buildFieldAccessors( type, pkgRegistry ); } catch ( Throwable e ) { kbuilder.addBuilderResult(new TypeDeclarationError(typeDescr, "Error creating field accessors for TypeDeclaration '" + type.getTypeName() + "' for type '" + type.getTypeName() + " : " + e.getMessage() + "'")); return false; } } processTypeAnnotations( kbuilder, pkgRegistry, typeDescr, type ); return true; } static void processTypeAnnotations( KnowledgeBuilderImpl kbuilder, PackageRegistry pkgRegistry, Annotated annotated, TypeDeclaration type ) { wireTimestampAccessor( kbuilder, annotated, type, pkgRegistry ); wireDurationAccessor( kbuilder, annotated, type, pkgRegistry ); configureExpirationOffset( kbuilder, annotated, type ); configurePropertyReactivity( kbuilder, annotated, type ); } protected void buildFieldAccessors(final TypeDeclaration type, final PackageRegistry pkgRegistry) throws SecurityException, IllegalArgumentException, InstantiationException, IllegalAccessException, IOException, ClassNotFoundException, NoSuchMethodException, InvocationTargetException, NoSuchFieldException { ClassDefinition cd = type.getTypeClassDef(); ClassFieldAccessorStore store = pkgRegistry.getPackage().getClassFieldAccessorStore(); for ( FieldDefinition attrDef : cd.getFieldsDefinitions() ) { ClassFieldAccessor accessor = store.getAccessor( cd.getDefinedClass().getName(), attrDef.getName() ); attrDef.setReadWriteAccessor( accessor ); } } private static void wireTimestampAccessor( KnowledgeBuilderImpl kbuilder, Annotated annotated, TypeDeclaration type, PackageRegistry pkgRegistry ) { Timestamp timestamp = annotated.getTypedAnnotation(Timestamp.class); if ( timestamp != null ) { BaseDescr typeDescr = annotated instanceof BaseDescr ? ( (BaseDescr) annotated ) : new BaseDescr(); String timestampField; try { timestampField = timestamp.value(); } catch (Exception e) { kbuilder.addBuilderResult(new TypeDeclarationError(typeDescr, e.getMessage())); return; } type.setTimestampAttribute( timestampField ); InternalKnowledgePackage pkg = pkgRegistry.getPackage(); MVELAnalysisResult results = getMvelAnalysisResult( kbuilder, typeDescr, type, pkgRegistry, timestampField, pkg ); if (results != null) { type.setTimestampExtractor(getFieldExtractor( type, timestampField, pkg, results )); } else { kbuilder.addBuilderResult(new TypeDeclarationError(typeDescr, "Error creating field accessors for timestamp field '" + timestamp + "' for type '" + type.getTypeName() + "'")); } } } private static void wireDurationAccessor( KnowledgeBuilderImpl kbuilder, Annotated annotated, TypeDeclaration type, PackageRegistry pkgRegistry ) { Duration duration = annotated.getTypedAnnotation(Duration.class); if (duration != null) { BaseDescr typeDescr = annotated instanceof BaseDescr ? ( (BaseDescr) annotated ) : new BaseDescr(); String durationField; try { durationField = duration.value(); } catch (Exception e) { kbuilder.addBuilderResult(new TypeDeclarationError(typeDescr, e.getMessage())); return; } type.setDurationAttribute(durationField); InternalKnowledgePackage pkg = pkgRegistry.getPackage(); MVELAnalysisResult results = getMvelAnalysisResult( kbuilder, typeDescr, type, pkgRegistry, durationField, pkg ); if (results != null) { type.setDurationExtractor(getFieldExtractor( type, durationField, pkg, results )); } else { kbuilder.addBuilderResult(new TypeDeclarationError(typeDescr, "Error processing @duration for TypeDeclaration '" + type.getFullName() + "': cannot access the field '" + durationField + "'")); } } } private static MVELAnalysisResult getMvelAnalysisResult( KnowledgeBuilderImpl kbuilder, BaseDescr typeDescr, TypeDeclaration type, PackageRegistry pkgRegistry, String durationField, InternalKnowledgePackage pkg ) { MVELDialect dialect = (MVELDialect) pkgRegistry.getDialectCompiletimeRegistry().getDialect("mvel"); PackageBuildContext context = new PackageBuildContext(); context.init(kbuilder, pkg, typeDescr, pkgRegistry.getDialectCompiletimeRegistry(), dialect, null); if (!type.isTypesafe()) { context.setTypesafe(false); } return (MVELAnalysisResult) context.getDialect().analyzeExpression( context, typeDescr, durationField, new BoundIdentifiers( type.getTypeClass() ) ); } private static InternalReadAccessor getFieldExtractor( TypeDeclaration type, String timestampField, InternalKnowledgePackage pkg, MVELAnalysisResult results ) { InternalReadAccessor reader = pkg.getClassFieldAccessorStore().getMVELReader( ClassUtils.getPackage( type.getTypeClass() ), type.getTypeClass().getName(), timestampField, type.isTypesafe(), results.getReturnType()); MVELDialectRuntimeData data = (MVELDialectRuntimeData) pkg.getDialectRuntimeRegistry().getDialectData("mvel"); data.addCompileable((MVELCompileable) reader); ((MVELCompileable) reader).compile(data); return reader; } private static void configureExpirationOffset( KnowledgeBuilderImpl kbuilder, Annotated annotated, TypeDeclaration type ) { Expires expires = annotated.getTypedAnnotation(Expires.class); if (expires != null) { String expiration; try { expiration = expires.value(); } catch (Exception e) { if (annotated instanceof BaseDescr) { kbuilder.addBuilderResult( new TypeDeclarationError( (BaseDescr)annotated, e.getMessage() ) ); } return; } long offset = TimeIntervalParser.parseSingle( expiration ); // @Expires( -1 ) means never expire type.setExpirationOffset(offset == -1L ? Long.MAX_VALUE : offset); type.setExpirationType( expires.policy() ); } } private static void configurePropertyReactivity( KnowledgeBuilderImpl kbuilder, Annotated annotated, TypeDeclaration type ) { PropertySpecificOption propertySpecificOption = kbuilder.getBuilderConfiguration().getPropertySpecificOption(); boolean propertyReactive = propertySpecificOption.isPropSpecific( annotated.hasAnnotation( PropertyReactive.class ), annotated.hasAnnotation( ClassReactive.class ) ); type.setPropertyReactive(propertyReactive); } }