diff --git a/enable-jdom.patch b/enable-jdom.patch
new file mode 100644
index 0000000..1120b25
--- /dev/null
+++ b/enable-jdom.patch
@@ -0,0 +1,81 @@
+--- src/main/java/freemarker/ext/jdom/NodeListModel.java.orig 2017-10-16 02:17:50.000000000 +0100
++++ src/main/java/freemarker/ext/jdom/NodeListModel.java 2017-12-06 18:53:07.877967201 +0000
+@@ -661,7 +661,7 @@
+ else if ("data".equals(localName))
+ attr = new Attribute("data", pi.getData());
+ else
+- attr = new Attribute(localName, pi.getValue(localName));
++ attr = new Attribute(localName, pi.getPseudoAttributeValue(localName));
+ } else if (node instanceof DocType) {
+ DocType doctype = (DocType) node;
+ if ("publicId".equals(localName))
+@@ -762,17 +762,17 @@
+
+ private static final Element getParent(Object node) {
+ if (node instanceof Element)
+- return((Element) node).getParent();
++ return((Element) node).getParentElement();
+ else if (node instanceof Attribute)
+ return((Attribute) node).getParent();
+ else if (node instanceof Text)
+- return((Text) node).getParent();
++ return((Text) node).getParentElement();
+ else if (node instanceof ProcessingInstruction)
+- return((ProcessingInstruction) node).getParent();
++ return((ProcessingInstruction) node).getParentElement();
+ else if (node instanceof Comment)
+- return((Comment) node).getParent();
++ return((Comment) node).getParentElement();
+ else if (node instanceof EntityRef)
+- return((EntityRef) node).getParent();
++ return((EntityRef) node).getParentElement();
+ else
+ // With 2.1 semantics it makes more sense to just return a null and let the core
+ // throw an InvalidReferenceException and the template writer can use ?exists etcetera. (JR)
+@@ -794,7 +794,7 @@
+ LinkedList list = new LinkedList();
+ do {
+ list.addFirst(parent);
+- parent = parent.getParent();
++ parent = parent.getParentElement();
+ } while (parent != null);
+ return list;
+ }
+@@ -808,7 +808,7 @@
+ list.addFirst(node);
+ do {
+ list.addFirst(parent);
+- parent = parent.getParent();
++ parent = parent.getParentElement();
+ } while (parent != null);
+ return list;
+ }
+@@ -861,7 +861,7 @@
+ Element parent = ((Attribute) node).getParent();
+ doc = parent == null ? null : parent.getDocument();
+ } else if (node instanceof Text) {
+- Element parent = ((Text) node).getParent();
++ Element parent = ((Text) node).getParentElement();
+ doc = parent == null ? null : parent.getDocument();
+ } else if (node instanceof Document)
+ doc = (Document) node;
+--- src/main/java/freemarker/ext/xml/_JdomNavigator.java.orig 2012-03-01 01:56:25.000000000 +0100
++++ src/main/java/freemarker/ext/xml/_JdomNavigator.java 2012-05-16 13:32:06.328559677 +0200
+@@ -122,7 +122,7 @@
+ } else if ("data".equals(localName)) {
+ result.add(new Attribute("data", pi.getData()));
+ } else {
+- result.add(new Attribute(localName, pi.getValue(localName)));
++ result.add(new Attribute(localName, pi.getPseudoAttributeValue(localName)));
+ }
+ } else if (node instanceof DocType) {
+ DocType doctype = (DocType) node;
+@@ -186,7 +186,7 @@
+ Element parent = ((Attribute) node).getParent();
+ return parent == null ? null : parent.getDocument();
+ } else if (node instanceof Text) {
+- Element parent = ((Text) node).getParent();
++ Element parent = ((Text) node).getParentElement();
+ return parent == null ? null : parent.getDocument();
+ } else if (node instanceof Document)
+ return node;
diff --git a/fix-javadoc-encoding.patch b/fix-javadoc-encoding.patch
new file mode 100644
index 0000000..727ad7c
--- /dev/null
+++ b/fix-javadoc-encoding.patch
@@ -0,0 +1,11 @@
+--- src/main/java/freemarker/template/LocalizedString.java.orig 2017-10-16 02:17:50.000000000 +0100
++++ src/main/java/freemarker/template/LocalizedString.java 2017-12-06 19:43:10.783714300 +0000
+@@ -33,7 +33,7 @@
+ * if "fr".equals(lang)
+ * return "oui";
+ * else if "de".equals(lang)
+- * return "sí";
++ * return "s\u00ED";
+ * else
+ * return "yes";
+ * }
diff --git a/freemarker-2.3.28.tar.gz b/freemarker-2.3.28.tar.gz
new file mode 100644
index 0000000..65ec13a
Binary files /dev/null and b/freemarker-2.3.28.tar.gz differ
diff --git a/freemarker.spec b/freemarker.spec
new file mode 100644
index 0000000..abf89bc
--- /dev/null
+++ b/freemarker.spec
@@ -0,0 +1,64 @@
+Name: freemarker
+Version: 2.3.28
+Release: 1
+Summary: The Apache FreeMarker Template Engine
+License: ASL 2.0
+URL: https://freemarker.apache.org/
+Source0: https://github.com/apache/incubator-freemarker/archive/v%{version}/%{name}-%{version}.tar.gz
+Patch1: jsp-api.patch
+Patch2: jython-compatibility.patch
+Patch3: fix-javadoc-encoding.patch
+Patch5: no-javarebel.patch
+Patch6: enable-jdom.patch
+Patch7: javacc-7.patch
+BuildArch: noarch
+BuildRequires: ant apache-parent apache-commons-logging aqute-bnd dom4j >= 1.6.1 hamcrest
+BuildRequires: ivy-local glassfish-jsp-api glassfish-servlet-api javacc >= 7.0 jaxen >= 1.1
+BuildRequires: jcl-over-slf4j jdom >= 1.0 junit jython log4j-over-slf4j rhino >= 1.6 slf4j
+BuildRequires: xalan-j2 >= 2.7.0
+%description
+Apache FreeMarker is a template engine: a Java library to generate text output
+(HTML web pages, e-mails, configuration files, source code, etc.) based on
+templates and changing data. Templates are written in the FreeMarker Template
+Language (FTL), which is a simple, specialized language (not a full-blown
+programming language like PHP).
+
+%package javadoc
+Summary: Javadoc for %{name}
+%description javadoc
+This package contains the API documentation for %{name}.
+
+%prep
+%setup -q
+find -type f -name "*.jar" -delete
+find -type f -name "*.class" -delete
+%patch1
+%patch2
+%patch3
+%patch5
+%patch6
+%patch7 -p1
+rm ivysettings.xml
+sed -i 's/cachepath conf="IDE"/cachepath conf="javadoc"/' build.xml
+sed -i '/conf name="IDE"/i' ivy.xml
+sed -i -e '/saxpath/d' -e '/avalon-logkit/d' ivy.xml
+rm src/main/java/freemarker/log/_AvalonLoggerFactory.java
+%mvn_file org.%{name}:%{name} %{name}
+
+%build
+ant -Divy.mode=local -Ddeps.available=true javacc jar javadoc maven-pom
+
+%install
+%mvn_artifact build/pom.xml build/%{name}.jar
+%mvn_install -J build/api
+
+%files -f .mfiles
+%doc README.md RELEASE-NOTES
+%license LICENSE NOTICE
+
+%files javadoc -f .mfiles-javadoc
+%license LICENSE NOTICE
+
+%changelog
+* Mon Aug 3 2020 yanan li - 2.3.28-1
+- Package init
diff --git a/freemarker.yaml b/freemarker.yaml
new file mode 100644
index 0000000..5f23f7f
--- /dev/null
+++ b/freemarker.yaml
@@ -0,0 +1,5 @@
+git_url: https://github.com/apache/incubator-freemarker.git
+version_control: github
+src_repo: apache/incubator-freemarker
+tag_prefix: "^v"
+seperator: "."
diff --git a/javacc-7.patch b/javacc-7.patch
new file mode 100644
index 0000000..1a90aa3
--- /dev/null
+++ b/javacc-7.patch
@@ -0,0 +1,46 @@
+From c119c7d20b46c91f2c661e4f3789194041c4ba16 Mon Sep 17 00:00:00 2001
+From: Michael Simacek
+Date: Tue, 4 Apr 2017 16:56:33 +0200
+Subject: [PATCH] Fix compatibility with javacc 7
+
+---
+ build.xml | 16 +++++++++++++++-
+ src/main/java/freemarker/core/TokenMgrError.java | 5 +++++
+ 2 files changed, 20 insertions(+), 1 deletion(-)
+
+diff --git a/build.xml b/build.xml
+index 6542da9..20d1d56 100644
+--- a/build.xml
++++ b/build.xml
+@@ -160,13 +160,26 @@
+
+
+
++
++
++
+
+
+-
++
++
++
+
+
+-
++
+
+
+-
+-
+-
+-
+-
+
+
+
+-
+
+@@ -104,9 +101,7 @@
+
+
+
+-
+-
+-
++
+
+
+
+@@ -183,7 +178,7 @@
+
+
+
+-
++
+
+
+
diff --git a/jython-compatibility.patch b/jython-compatibility.patch
new file mode 100644
index 0000000..bc36e05
--- /dev/null
+++ b/jython-compatibility.patch
@@ -0,0 +1,72 @@
+--- ivy.xml.orig 2017-12-06 19:20:16.456335648 +0000
++++ ivy.xml 2017-12-06 19:20:59.320159647 +0000
+@@ -49,12 +49,6 @@
+ description="for building FreeMarker with JSP 2.1 support"
+ />
+
+-
+-
+
+@@ -106,9 +100,6 @@
+
+
+
+-
+-
+-
+
+
+
+--- build.xml 2017-12-06 19:19:56.790416398 +0000
++++ build.xml.orig 2017-12-06 19:25:06.163149500 +0000
+@@ -318,41 +318,21 @@
+ freemarker/ext/jsp/FreeMarkerJspFactory2.java"
+ />
+
+-
++
+
+-
+-
+-
+
+-
+-
+-
+
+
+
+-
+-
+
+
+