diff --git a/lombok-custom/pom.xml b/lombok-custom/pom.xml
new file mode 100644
index 0000000000..41bd042a5e
--- /dev/null
+++ b/lombok-custom/pom.xml
@@ -0,0 +1,58 @@
+
+
+
+ parent-modules
+ com.baeldung
+ 1.0.0-SNAPSHOT
+
+
+ 4.0.0
+ lombok-custom
+ 0.1-SNAPSHOT
+
+
+
+ org.projectlombok
+ lombok
+ 1.14.8
+ provided
+
+
+
+ org.kohsuke.metainf-services
+ metainf-services
+ 1.8
+
+
+
+ org.eclipse.jdt
+ core
+ 3.3.0-v_771
+
+
+
+
+
+ default-tools.jar
+
+
+ java.vendor
+ Oracle Corporation
+
+
+
+
+ com.sun
+ tools
+ ${java.version}
+ system
+ ${java.home}/../lib/tools.jar
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/lombok-custom/src/main/java/com/baeldung/singleton/Singleton.java b/lombok-custom/src/main/java/com/baeldung/singleton/Singleton.java
new file mode 100644
index 0000000000..2d2fd0ffb9
--- /dev/null
+++ b/lombok-custom/src/main/java/com/baeldung/singleton/Singleton.java
@@ -0,0 +1,10 @@
+package com.baeldung.singleton;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Target;
+
+@Target(ElementType.TYPE)
+public @interface Singleton {
+
+
+}
diff --git a/lombok-custom/src/main/java/com/baeldung/singleton/handlers/SingletonEclipseHandler.java b/lombok-custom/src/main/java/com/baeldung/singleton/handlers/SingletonEclipseHandler.java
new file mode 100644
index 0000000000..03cdd707a4
--- /dev/null
+++ b/lombok-custom/src/main/java/com/baeldung/singleton/handlers/SingletonEclipseHandler.java
@@ -0,0 +1,124 @@
+package com.baeldung.singleton.handlers;
+
+import com.baeldung.singleton.Singleton;
+import lombok.core.AnnotationValues;
+import lombok.eclipse.EclipseAnnotationHandler;
+import lombok.eclipse.EclipseNode;
+import lombok.eclipse.handlers.EclipseHandlerUtil;
+import org.eclipse.jdt.internal.compiler.ast.AllocationExpression;
+import org.eclipse.jdt.internal.compiler.ast.Annotation;
+import org.eclipse.jdt.internal.compiler.ast.ConstructorDeclaration;
+import org.eclipse.jdt.internal.compiler.ast.FieldDeclaration;
+import org.eclipse.jdt.internal.compiler.ast.FieldReference;
+import org.eclipse.jdt.internal.compiler.ast.MethodDeclaration;
+import org.eclipse.jdt.internal.compiler.ast.ReturnStatement;
+import org.eclipse.jdt.internal.compiler.ast.SingleNameReference;
+import org.eclipse.jdt.internal.compiler.ast.Statement;
+import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration;
+import org.eclipse.jdt.internal.compiler.ast.TypeReference;
+import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
+import org.kohsuke.MetaInfServices;
+
+import static lombok.eclipse.Eclipse.ECLIPSE_DO_NOT_TOUCH_FLAG;
+import static org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants.AccFinal;
+import static org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants.AccPrivate;
+import static org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants.AccStatic;
+
+@MetaInfServices(EclipseAnnotationHandler.class)
+public class SingletonEclipseHandler extends EclipseAnnotationHandler {
+
+
+ @Override
+ public void handle(AnnotationValues annotation, Annotation ast, EclipseNode annotationNode) {
+
+ //remove annotation
+ EclipseHandlerUtil.unboxAndRemoveAnnotationParameter(ast, "onType", "@Singleton(onType=", annotationNode);
+
+ //add private constructor
+ EclipseNode singletonClass = annotationNode.up();
+
+ TypeDeclaration singletonClassType = (TypeDeclaration) singletonClass.get();
+ ConstructorDeclaration constructor = addConstructor(singletonClass, singletonClassType);
+
+ TypeReference singletonTypeRef = EclipseHandlerUtil.cloneSelfType(singletonClass, singletonClassType);
+
+ //add inner class
+
+ //add instance field
+ StringBuilder sb = new StringBuilder();
+ sb.append(singletonClass.getName());
+ sb.append("Holder");
+ String innerClassName = sb.toString();
+ TypeDeclaration innerClass = new TypeDeclaration(singletonClassType.compilationResult);
+ innerClass.modifiers = AccPrivate | AccStatic;
+ innerClass.name = innerClassName.toCharArray();
+
+ FieldDeclaration instanceVar = addInstanceVar(constructor, singletonTypeRef, innerClass);
+
+ FieldDeclaration[] declarations = new FieldDeclaration[]{instanceVar};
+ innerClass.fields = declarations;
+
+ EclipseHandlerUtil.injectType(singletonClass, innerClass);
+
+ addFactoryMethod(singletonClass, singletonClassType, singletonTypeRef, innerClass, instanceVar);
+
+
+ }
+
+ private void addFactoryMethod(EclipseNode singletonClass, TypeDeclaration astNode, TypeReference typeReference, TypeDeclaration innerClass, FieldDeclaration field) {
+ MethodDeclaration factoryMethod = new MethodDeclaration(astNode.compilationResult);
+ factoryMethod.modifiers = AccStatic | ClassFileConstants.AccPublic;
+ factoryMethod.returnType = typeReference;
+ factoryMethod.sourceStart = astNode.sourceStart;
+ factoryMethod.sourceEnd = astNode.sourceEnd;
+ factoryMethod.selector = "getInstance".toCharArray();
+ factoryMethod.bits = ECLIPSE_DO_NOT_TOUCH_FLAG;
+
+ long pS = factoryMethod.sourceStart;
+ long pE = factoryMethod.sourceEnd;
+ long p = (long) pS << 32 | pE;
+
+ FieldReference ref = new FieldReference(field.name, p);
+ ref.receiver = new SingleNameReference(innerClass.name, p);
+
+ ReturnStatement statement = new ReturnStatement(ref, astNode.sourceStart, astNode.sourceEnd);
+
+ factoryMethod.statements = new Statement[]{statement};
+
+ EclipseHandlerUtil.injectMethod(singletonClass, factoryMethod);
+ }
+
+ private FieldDeclaration addInstanceVar(ConstructorDeclaration constructor, TypeReference typeReference, TypeDeclaration innerClass) {
+ FieldDeclaration field = new FieldDeclaration();
+ field.modifiers = AccPrivate | AccStatic | AccFinal;
+ field.name = "INSTANCE".toCharArray();
+
+ field.type = typeReference;
+
+ AllocationExpression exp = new AllocationExpression();
+ exp.type = typeReference;
+ exp.binding = constructor.binding;
+ exp.sourceStart = innerClass.sourceStart;
+ exp.sourceEnd = innerClass.sourceEnd;
+
+ field.initialization = exp;
+ return field;
+ }
+
+ private ConstructorDeclaration addConstructor(EclipseNode singletonClass, TypeDeclaration astNode) {
+ ConstructorDeclaration constructor = new ConstructorDeclaration(astNode.compilationResult);
+ constructor.modifiers = AccPrivate;
+ constructor.selector = astNode.name;
+ constructor.sourceStart = astNode.sourceStart;
+ constructor.sourceEnd = astNode.sourceEnd;
+ constructor.thrownExceptions = null;
+ constructor.typeParameters = null;
+ constructor.bits |= ECLIPSE_DO_NOT_TOUCH_FLAG;
+ constructor.bodyStart = constructor.declarationSourceStart = constructor.sourceStart = astNode.sourceStart;
+ constructor.bodyEnd = constructor.declarationSourceEnd = constructor.sourceEnd = astNode.sourceEnd;
+ constructor.arguments = null;
+
+ EclipseHandlerUtil.injectMethod(singletonClass, constructor);
+ return constructor;
+ }
+}
diff --git a/lombok-custom/src/main/java/com/baeldung/singleton/handlers/SingletonJavacHandler.java b/lombok-custom/src/main/java/com/baeldung/singleton/handlers/SingletonJavacHandler.java
new file mode 100644
index 0000000000..1f4cf0ea58
--- /dev/null
+++ b/lombok-custom/src/main/java/com/baeldung/singleton/handlers/SingletonJavacHandler.java
@@ -0,0 +1,108 @@
+package com.baeldung.singleton.handlers;
+
+import com.baeldung.singleton.Singleton;
+import com.sun.tools.javac.code.Flags;
+import com.sun.tools.javac.tree.JCTree;
+import com.sun.tools.javac.util.Context;
+import com.sun.tools.javac.util.List;
+import com.sun.tools.javac.util.ListBuffer;
+import lombok.core.AnnotationValues;
+import lombok.javac.Javac8BasedLombokOptions;
+import lombok.javac.JavacAnnotationHandler;
+import lombok.javac.JavacNode;
+import lombok.javac.JavacTreeMaker;
+import lombok.javac.handlers.JavacHandlerUtil;
+import org.kohsuke.MetaInfServices;
+
+import static lombok.javac.handlers.JavacHandlerUtil.deleteAnnotationIfNeccessary;
+import static lombok.javac.handlers.JavacHandlerUtil.deleteImportFromCompilationUnit;
+
+@MetaInfServices(JavacAnnotationHandler.class)
+public class SingletonJavacHandler extends JavacAnnotationHandler {
+
+ @Override
+ public void handle(AnnotationValues annotation, JCTree.JCAnnotation ast, JavacNode annotationNode) {
+
+ Context context = annotationNode.getContext();
+
+ Javac8BasedLombokOptions options = Javac8BasedLombokOptions.replaceWithDelombokOptions(context);
+ options.deleteLombokAnnotations();
+
+ //remove annotation
+ deleteAnnotationIfNeccessary(annotationNode, Singleton.class);
+ //remove import
+ deleteImportFromCompilationUnit(annotationNode, "lombok.AccessLevel");
+
+ //private constructor
+ JavacNode singletonClass = annotationNode.up();
+ JavacTreeMaker singletonClassTreeMaker = singletonClass.getTreeMaker();
+
+ addPrivateConstructor(singletonClass, singletonClassTreeMaker);
+
+ //singleton holder
+ JavacNode holderInnerClass = addInnerClass(singletonClass, singletonClassTreeMaker);
+
+ //inject static field to this
+ addInstanceVar(singletonClass, singletonClassTreeMaker, holderInnerClass);
+
+ //add factory method
+ addFactoryMethod(singletonClass, singletonClassTreeMaker, holderInnerClass);
+ }
+
+ private void addFactoryMethod(JavacNode singletonClass, JavacTreeMaker singletonClassTreeMaker, JavacNode holderInnerClass) {
+ JCTree.JCModifiers modifiers = singletonClassTreeMaker.Modifiers(Flags.PUBLIC | Flags.STATIC);
+
+ JCTree.JCClassDecl singletonClassDecl = (JCTree.JCClassDecl) singletonClass.get();
+ JCTree.JCIdent singletonClassType = singletonClassTreeMaker.Ident(singletonClassDecl.name);
+
+ JCTree.JCBlock block = addReturnBlock(singletonClassTreeMaker, holderInnerClass);
+
+ JCTree.JCMethodDecl factoryMethod = singletonClassTreeMaker.MethodDef(modifiers, singletonClass.toName("getInstance"), singletonClassType, List.nil(), List.nil(), List.nil(), block, null);
+ JavacHandlerUtil.injectMethod(singletonClass, factoryMethod);
+ }
+
+ private JCTree.JCBlock addReturnBlock(JavacTreeMaker singletonClassTreeMaker, JavacNode holderInnerClass) {
+
+ JCTree.JCClassDecl holderInnerClassDecl = (JCTree.JCClassDecl) holderInnerClass.get();
+ JavacTreeMaker holderInnerClassTreeMaker = holderInnerClass.getTreeMaker();
+ JCTree.JCIdent holderInnerClassType = holderInnerClassTreeMaker.Ident(holderInnerClassDecl.name);
+
+ JCTree.JCFieldAccess instanceVarAccess = holderInnerClassTreeMaker.Select(holderInnerClassType, holderInnerClass.toName("INSTANCE"));
+ JCTree.JCReturn returnValue = singletonClassTreeMaker.Return(instanceVarAccess);
+
+ ListBuffer statements = new ListBuffer<>();
+ statements.append(returnValue);
+
+ return singletonClassTreeMaker.Block(0L, statements.toList());
+ }
+
+
+ private void addInstanceVar(JavacNode singletonClass, JavacTreeMaker singletonClassTM, JavacNode holderClass) {
+ JCTree.JCModifiers fieldMod = singletonClassTM.Modifiers(Flags.PRIVATE | Flags.STATIC | Flags.FINAL);
+
+ JCTree.JCClassDecl singletonClassDecl = (JCTree.JCClassDecl) singletonClass.get();
+ JCTree.JCIdent singletonClassType = singletonClassTM.Ident(singletonClassDecl.name);
+
+ JCTree.JCNewClass newKeyword = singletonClassTM.NewClass(null, List.nil(), singletonClassType, List.nil(), null);
+
+ JCTree.JCVariableDecl instanceVar = singletonClassTM.VarDef(fieldMod, singletonClass.toName("INSTANCE"), singletonClassType, newKeyword);
+ JavacHandlerUtil.injectField(holderClass, instanceVar);
+ }
+
+ private JavacNode addInnerClass(JavacNode singletonClass, JavacTreeMaker singletonTM) {
+ JCTree.JCModifiers modifiers = singletonTM.Modifiers(Flags.PRIVATE | Flags.STATIC);
+ String innerClassName = singletonClass.getName() + "Holder";
+ JCTree.JCClassDecl innerClassDecl = singletonTM.ClassDef(modifiers, singletonClass.toName(innerClassName), List.nil(), null, List.nil(), List.nil());
+ return JavacHandlerUtil.injectType(singletonClass, innerClassDecl);
+ }
+
+ private void addPrivateConstructor(JavacNode singletonClass, JavacTreeMaker singletonTM) {
+ JCTree.JCModifiers modifiers = singletonTM.Modifiers(Flags.PRIVATE);
+ JCTree.JCBlock block = singletonTM.Block(0L, List.nil());
+ JCTree.JCMethodDecl constructor = singletonTM.MethodDef(modifiers, singletonClass.toName(""), null, List.nil(), List.nil(), List.nil(), block, null);
+
+ JavacHandlerUtil.injectMethod(singletonClass, constructor);
+ }
+
+
+}
diff --git a/pom.xml b/pom.xml
index 99dae9decb..03a66080c2 100644
--- a/pom.xml
+++ b/pom.xml
@@ -7,7 +7,10 @@
com.baeldung
parent-modules
1.0.0-SNAPSHOT
- parent-modules
+
+ lombok-custom
+
+ parent-modules
pom