Skip to main content

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [List Home]
Re: [eclipselink-users] programmatic customization of mapping meta-data at runtime

Hi Olaf,

  Just to share my learning with you.

   I create a proxy around the CGLIB (because it help me create proxy and
its not mandatory to implement interface).

  I hooked up the proxy using the DescriptorCustomizer. Then its mandatory
to provide the copy policy also.

  Then another issue, if it is require to cache proxy entity into a cache
(say tangasol/jboss cache), it create problem. (This can be avoided   depend
on the cahing technology in use).

  There is another approach, which is the best, enriching the domain model
by using spring property configurator. But we have to overide this class and
make it dynamic. In this dynamic configurator provide the logic for handling
the dynamic part of the entity.

 I also like you Aspect oriented approach and I think you will be using
inter-type declarations. I think, this i also good idea.

Note :- My framework uses spring framework so does provide me few good
flexibilities.

Regards,
Gaurav Malhotra

Olaf Otto wrote:
> 
> Hi Guarav,
> 
> I took some time to look at the stacktrace generated during the failure 
> of the pre-deployment and examined the classes involved.
> I am pretty sure now that eclipselink does not provide a programmatic 
> customization hook for entity meta-data before pre-deployment.
> 
> This is, of course, a correct behavior - we are actually trying to do 
> something that clearly violates the spec - declaring non-entity classes 
> as entities.
> 
> I will try now try to create an aspect that can be woven into one of the 
> classes involved in meta-data processing  to  obtain a callback hook to 
> be able to customize the meta-data of any declared entity.
> 
> I will post an update on this as soon as i get this to function.
> 
> Regards,
> Olaf
> 
> Gaurav Malhotra wrote:
>> Hi,
>>     I am also trying to achieve the same for long but with no success.
>>
>>     Say for example there is an entity deifined like the following
>>
>>    public class Issue implements Serializable{
>>
>> 	@Id
>>       @GeneratedValue
>> 	private int id;
>> 	@SuppressWarnings("unused")
>> 	@Version
>> 	private int version;
>> 	private String comments;
>> 	private String condition;
>> 	private int copies = 1;
>> 	private int issueNum;
>> 	private double pricePaid;
>> 	@Column(name="TITLE_ID")
>> 	private int titleId;
>> 	private BigDecimal value;	
>>
>>
>>    On this entity I want to add a new field “Priority” ( free field)
>>
>>
>>
>>     I tried using DescriptorCustomizer and SessionCustomizer callbacks to
>> customize entity: -
>>
>> 		
>> I create free fields like
>>
>> 		String klassName = klass.getSimpleName();
>> 		InstantClassLoader icl = new
>>   // Add new field to the class Issue
>>  InstantClassLoader(klassName,"priority");
>> 	
>> icl.setBasePackageNameToStartScan("org.eclipse.persistence.example.jpa.coherence.comics.model.annotated");
>> 		Object retObj = null;
>> 		
>> 		try {
>> 			retObj = icl.loadClass(klassName).newInstance();
>> 		} catch (InstantiationException e) {
>> 			e.printStackTrace();
>> 			throw new RuntimeException(e);
>> 		} catch (IllegalAccessException e) {
>> 			e.printStackTrace();
>> 			throw new RuntimeException(e);
>> 		} catch (ClassNotFoundException e) {
>> 			e.printStackTrace();
>> 			throw new RuntimeException(e);
>> 		}
>> 		
>> 		return retObj;
>>
>>      
>>     My InstantClassLoader is based on  ASM framework and adding fields
>> like
>>
>> 	public Class<?> addFieldToClass(String className, String fieldName) {
>> 		Resource[] rs = null;
>> 		try {
>> 			rs = DiscoverUtils.resolveResource(className);
>> 		} catch (Exception e) {
>> 			throw new RuntimeException(e);
>> 		}
>>
>> 		if (rs.length == 0) {
>> 			String pkg = DiscoverUtils.discoverClass(className.split("\\.")[0],
>> 					basePackageNameToStartScan);
>> 			System.err.println(pkg);
>>
>> 			String resPath = ClassUtils.convertClassNameToResourcePath(pkg);
>> 			System.err.println(resPath);
>>
>> 			return addFieldToClass(resPath + ".class", fieldName);
>> 		}
>>
>> 		Assert.isTrue(rs.length == 1, "More one " + className + " exists");
>> 		InputStream is = null;
>> 		try {
>> 			is = rs[0].getInputStream();
>> 		} catch (IOException e) {
>> 			throw new RuntimeException(e);
>> 		}
>>
>> 		Assert.notNull(is, "Not able create input stream for resource "
>> 				+ className);
>>
>> 		byte[] b;
>> 		String klassName = className.split("\\.")[0];
>> 		Assert.hasLength(klassName, "Wrong class name " + className);
>> 		// adapts the class on the fly
>> 		try {
>> 			ClassReader cr = new ClassReader(is);
>> 			ClassWriter cw = new ClassWriter(cr, ClassWriter.COMPUTE_MAXS);
>> 			CustomClassAdapter cca = new CustomClassAdapter(cw);
>> 			ClassVisitor cv = new ClassAdapter(cca);
>> 			cca.visitCode(fieldName, klassName);
>>
>> 			cr.accept(cv, 0);
>> 			b = cw.toByteArray();
>>
>> 		} catch (IOException e) {
>> 			throw new RuntimeException(e);
>> 		}
>>
>> 		// TODO remove : just for debugging
>> 		try {
>> 			FileOutputStream fos = new FileOutputStream(rs[0].getURI()
>> 					.getPath()
>> 					+ ".adapted");
>> 			fos.write(b);
>> 			fos.close();
>>
>> 			FileOutputStream fos1 = new FileOutputStream(rs[0].getURI()
>> 					.getPath()
>> 					+ ".adapted1");
>> 			fos1.write(is.read(new byte[99999]));
>> 			fos1.close();
>>
>> 		} catch (Exception e) {
>> 			e.printStackTrace();
>> 		}
>>
>> 		Class<?> klass = defineClass(ClassUtils
>> 				.convertResourcePathToClassName(klassName), b, 0, b.length);
>>
>> 		return klass;
>> 	}
>>
>> 	public static class CustomClassAdapter extends ClassAdapter implements
>> 			Opcodes {
>> 		private String owner;
>> 		private ClassVisitor cv;
>>
>> 		public CustomClassAdapter(ClassVisitor cv) {
>> 			super(cv);
>> 			this.cv = cv;
>> 		}
>>
>> 		public void visitCode(String fieldName, String klassName) {
>> 			String desc = "Ljava/lang/String;";
>> 			Type t = Type.getType(desc);
>> 			int size = t.getSize();
>>
>> 			String fDesc = "Ljava/lang/String;";
>> 			FieldVisitor fv = cv.visitField(Opcodes.ACC_PRIVATE, fieldName,
>> 					fDesc, null, null);
>> 			fv.visitEnd();
>>
>> 			// generates getter method
>> 			String gDesc = "()" + desc;
>> 			MethodVisitor gv = cv.visitMethod(Opcodes.ACC_PUBLIC, "get"
>> 					+ fieldName.substring(0, 1).toUpperCase()
>> 					+ fieldName.substring(1, fieldName.length()), gDesc, null,
>> 					null);
>> 			gv.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/System", "err",
>> 					"Ljava/io/PrintStream;");
>> 			gv.visitLdcInsn("get" + fieldName + " called");
>> 			gv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/io/PrintStream",
>> 					"println", "(Ljava/lang/String;)V");
>> 			gv.visitVarInsn(Opcodes.ALOAD, 0);
>>
>> 			gv.visitFieldInsn(Opcodes.GETFIELD, klassName, fieldName, desc);
>> 			gv.visitInsn(t.getOpcode(Opcodes.IRETURN));
>>
>> 			gv.visitMaxs(size + 1, size + 1);
>> 			gv.visitEnd();
>>
>> 			// generates setter method
>> 			String sDesc = "(" + desc + ")V";
>> 			MethodVisitor sv = cv.visitMethod(Opcodes.ACC_PUBLIC, "set"
>> 					+ fieldName.substring(0, 1).toUpperCase()
>> 					+ fieldName.substring(1, fieldName.length()), sDesc, null,
>> 					null);
>> 			sv.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/System", "err",
>> 					"Ljava/io/PrintStream;");
>> 			sv.visitLdcInsn("set" + fieldName + " called");
>> 			sv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/io/PrintStream",
>> 					"println", "(Ljava/lang/String;)V");
>> 			sv.visitVarInsn(Opcodes.ALOAD, 0);
>> 			sv.visitVarInsn(t.getOpcode(Opcodes.ILOAD), 1);
>> 			sv.visitFieldInsn(Opcodes.PUTFIELD, klassName, fieldName, desc);
>> 			sv.visitInsn(Opcodes.RETURN);
>> 			sv.visitMaxs(1 + size, 1 + size);
>> 			sv.visitEnd();
>>
>> 		}
>>
>> 		@Override
>> 		public AnnotationVisitor visitAnnotation(String param, boolean visible)
>> {
>> 			return super.visitAnnotation(param, visible);
>> 		}
>>
>> 		@Override
>> 		public FieldVisitor visitField(final int access, final String name,
>> 				final String desc, final String signature, final Object value) {
>> 			FieldVisitor fv = super.visitField(access, name, desc, signature,
>> value);
>> 			CustomFieldVisitor cfv = new CustomFieldVisitor(fv);
>> 			return cfv ;
>> 		}
>>
>> 		@Override
>> 		public MethodVisitor visitMethod(final int access, final String name,
>> 				final String desc, final String signature,
>> 				final String[] exceptions) {
>> 			MethodVisitor mv = cv.visitMethod(access, name, desc, signature,
>> 					exceptions);
>> 			return new MethodAdapter(mv) {
>> 				public AnnotationVisitor visitParameterAnnotation(
>> 						final int parameter, final String desc,
>> 						final boolean visible) {
>> 					AnnotationVisitor av;
>> 					av = mv.visitParameterAnnotation(parameter, desc, visible);
>> 					return av;
>> 				}
>> 				
>> 			};
>> 			
>> 		}
>>
>> 	}
>> 	
>> 	public static class CustomFieldVisitor implements FieldVisitor {
>> 		private FieldVisitor fv;
>> 		public CustomFieldVisitor(FieldVisitor fv)  {
>> 			this.fv = fv;
>> 		}
>> 		@Override
>> 		public AnnotationVisitor visitAnnotation(String dec, boolean visible) {
>> 			return fv.visitAnnotation(dec, visible);
>> 		}
>>
>> 		@Override
>> 		public void visitAttribute(Attribute attr) {
>> 			fv.visitAttribute(attr);
>> 		}
>>
>> 		@Override
>> 		public void visitEnd() {
>> 			fv.visitEnd();
>> 		}
>> 	}
>>
>> }
>>
>>
>>
>>
>>
>> Olaf Otto wrote:
>>   
>>> Hi Tom,
>>>
>>> I am aware of the Descriptor customizers, but as far as i know these are 
>>> invoked after the pre-deployment.
>>> Currently, the classes modified by the framework cause the meta-data 
>>> processing to fail during processORMetadata as they contain 
>>> non-serializable fields that cannot be persisted (some of them reference 
>>> custom non-entity types that cannot be serialized).
>>>
>>> Thanks and regards,
>>> Olaf
>>>
>>> Tom Ware wrote:
>>>     
>>>> Hi Olaf,
>>>>
>>>>   There is a model built by our meta data processing that can be 
>>>> changed.  In essence, we build a bunch of instances of a class called 
>>>> ClassDescriptor.  These ClassDescriptors hold all the mappings we 
>>>> use.  We have a mechanism called a DescriptorCustomizer that provides 
>>>> a callback that will allow you to make changes to any of your 
>>>> descriptors.  Here is some documentation to get you started.
>>>>
>>>> http://wiki.eclipse.org/Configuring_a_Descriptor_(ELUG)#Configuring_a_Descriptor_Customizer_Class 
>>>>
>>>>
>>>>   Although, DescriptorCustomizers are invoked after our meta data 
>>>> processing has executed, I believe you should be able to use them.  
>>>> The idea would be that you allow metadata processing to complete and 
>>>> then customize the descriptors as you see fit.  Is there any 
>>>> particular reason why you would like to see the customization 
>>>> mechanism fire before the metadata is processed?
>>>>
>>>>
>>>> -Tom
>>>>
>>>> Olaf Otto wrote:
>>>>       
>>>>> Hi Tom,
>>>>>
>>>>> Thank you for your reply.
>>>>>
>>>>> Tom Ware wrote:
>>>>>         
>>>>>> Hi Olaf,
>>>>>>
>>>>>>   What kinds of things do you want to do in your meta-data 
>>>>>> modification?
>>>>>>           
>>>>> I think it will mostly be modifying the mapping type of certain 
>>>>> fields (for example changing it from basic to N:N or a custom 
>>>>> mapping), excluding fields etc.
>>>>>         
>>>>>>   Depending on what you want to do, there may be several ways to 
>>>>>> make modifications to the metadata.
>>>>>>
>>>>>> 1. There is a Customization mechanism that allows you to customize 
>>>>>> the mappings that are created by our meta-data processing
>>>>>>           
>>>>> Very interesting - which specific mechanism are you referring to?
>>>>>         
>>>>>> 2. There is an event mechanism the provides you with callbacks at 
>>>>>> certain stages of our initialization
>>>>>>           
>>>>> I have seen the event mechanism - but will this actually work before 
>>>>> the meta data of the entities has been processed?
>>>>>         
>>>>>> 3. You can specify your mappings in XML. (with the option of having 
>>>>>> the XML be the only source of mappings)
>>>>>>
>>>>>>           
>>>>> I know, but this is not an option, since this would require custom 
>>>>> mappings for framework users affecting fields they cannot see. 
>>>>> Furthermore, the fields are subject to a lot of changes when altering 
>>>>> classes. Basically, i must identify the generated fields by a certain 
>>>>> prefix and handle them with regard to their content.
>>>>>         
>>>>>>   You should also be aware that some performance features in 
>>>>>> EclipseLink are enabled by byte-code weaving.
>>>>>>
>>>>>>           
>>>>> Indeed, i have seen that using the eclipse agent has a lot of 
>>>>> advantages. But there are a lot of complications with java agents and 
>>>>> the framework i don't wish to address by now.
>>>>>         
>>>>>> -Tom
>>>>>>
>>>>>>           
>>>>> Regards,
>>>>> Olaf
>>>>>
>>>>>         
>>>>>> Olaf Otto wrote:
>>>>>>           
>>>>>>> Hello eclipse users!
>>>>>>>
>>>>>>> I have a framework that makes massive use of aspect-oriented 
>>>>>>> programming and code generation. This includes a significant amount 
>>>>>>> of additional fields being created in various classes (of a project 
>>>>>>> using the framework, not in the framework itself) by a java agent 
>>>>>>> at class loading time.  These fields are primarily generated for 
>>>>>>> internal framework purposes and are thus invisible to a framework 
>>>>>>> user.
>>>>>>>
>>>>>>> Most of these fields are references to non-serializable resp. 
>>>>>>> custom types and must be handled in a non-standard fashion for 
>>>>>>> persistence.
>>>>>>>
>>>>>>> Now here is the question: Is there a way to hook into the meta-data 
>>>>>>> processing occurring during predeployment? More precisely, i would 
>>>>>>> like to access and modify the persistence meta-data of entities 
>>>>>>> before processor.processORMMetadata() is invoked in the 
>>>>>>> processORMetadata of the PersistenceUnitProcessor class.
>>>>>>>
>>>>>>> Thank you and regards,
>>>>>>> Olaf
>>>>>>>
>>>>>>> _______________________________________________
>>>>>>> eclipselink-users mailing list
>>>>>>> eclipselink-users@xxxxxxxxxxx
>>>>>>> https://dev.eclipse.org/mailman/listinfo/eclipselink-users
>>>>>>>             
>>>>>> _______________________________________________
>>>>>> eclipselink-users mailing list
>>>>>> eclipselink-users@xxxxxxxxxxx
>>>>>> https://dev.eclipse.org/mailman/listinfo/eclipselink-users
>>>>>>           
>>>>> _______________________________________________
>>>>> eclipselink-users mailing list
>>>>> eclipselink-users@xxxxxxxxxxx
>>>>> https://dev.eclipse.org/mailman/listinfo/eclipselink-users
>>>>>         
>>>> _______________________________________________
>>>> eclipselink-users mailing list
>>>> eclipselink-users@xxxxxxxxxxx
>>>> https://dev.eclipse.org/mailman/listinfo/eclipselink-users
>>>>       
>>> _______________________________________________
>>> eclipselink-users mailing list
>>> eclipselink-users@xxxxxxxxxxx
>>> https://dev.eclipse.org/mailman/listinfo/eclipselink-users
>>>
>>>
>>>     
>>
>>   
> 
> _______________________________________________
> eclipselink-users mailing list
> eclipselink-users@xxxxxxxxxxx
> https://dev.eclipse.org/mailman/listinfo/eclipselink-users
> 
> 

-- 
View this message in context: http://www.nabble.com/programmatic-customization-of-mapping-meta-data-at-runtime-tp20505808p20528796.html
Sent from the EclipseLink - Users mailing list archive at Nabble.com.



Back to the top