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,
    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
> 
> 

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



Back to the top