├── .gitignore ├── .travis.yml ├── README.md ├── pom.xml └── src ├── main └── java │ └── com │ └── itranswarp │ └── compiler │ ├── JavaStringCompiler.java │ ├── MemoryClassLoader.java │ └── MemoryJavaFileManager.java └── test └── java └── com └── itranswarp ├── compiler └── JavaStringCompilerTest.java └── on └── the └── fly ├── BeanProxy.java └── User.java /.gitignore: -------------------------------------------------------------------------------- 1 | # Java.gitignore ############################################################## 2 | 3 | *.class 4 | 5 | # Mobile Tools for Java (J2ME) 6 | .mtj.tmp/ 7 | 8 | # Package Files # 9 | *.jar 10 | *.war 11 | *.ear 12 | 13 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 14 | hs_err_pid* 15 | 16 | # Maven.gitignore ############################################################# 17 | 18 | target/ 19 | pom.xml.tag 20 | pom.xml.releaseBackup 21 | pom.xml.versionsBackup 22 | pom.xml.next 23 | release.properties 24 | dependency-reduced-pom.xml 25 | buildNumber.properties 26 | .mvn/timing.properties 27 | 28 | # Eclipse.gitignore ########################################################### 29 | 30 | *.pydevproject 31 | .metadata 32 | .gradle 33 | bin/ 34 | tmp/ 35 | *.tmp 36 | *.bak 37 | *.swp 38 | *~.nib 39 | local.properties 40 | .settings/ 41 | .loadpath 42 | 43 | # Eclipse Core 44 | .project 45 | 46 | # External tool builders 47 | .externalToolBuilders/ 48 | .recommenders/ 49 | 50 | # Locally stored "Eclipse launch configurations" 51 | *.launch 52 | 53 | # CDT-specific 54 | .cproject 55 | 56 | # JDT-specific (Eclipse Java Development Tools) 57 | .classpath 58 | 59 | # Java annotation processor (APT) 60 | .factorypath 61 | 62 | # PDT-specific 63 | .buildpath 64 | 65 | # sbteclipse plugin 66 | .target 67 | 68 | # TeXlipse plugin 69 | .texlipse 70 | 71 | # NetBeans.gitignore ########################################################## 72 | 73 | nbproject/private/ 74 | build/ 75 | nbbuild/ 76 | dist/ 77 | nbdist/ 78 | nbactions.xml 79 | nb-configuration.xml 80 | .nb-gradle/ 81 | 82 | # JetBrains.gitignore ######################################################### 83 | 84 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio 85 | 86 | *.iml 87 | 88 | ## Directory-based project format: 89 | .idea/ 90 | # if you remove the above rule, at least ignore the following: 91 | 92 | # User-specific stuff: 93 | # .idea/workspace.xml 94 | # .idea/tasks.xml 95 | # .idea/dictionaries 96 | 97 | # Sensitive or high-churn files: 98 | # .idea/dataSources.ids 99 | # .idea/dataSources.xml 100 | # .idea/sqlDataSources.xml 101 | # .idea/dynamic.xml 102 | # .idea/uiDesigner.xml 103 | 104 | # Gradle: 105 | # .idea/gradle.xml 106 | # .idea/libraries 107 | 108 | # Mongo Explorer plugin: 109 | # .idea/mongoSettings.xml 110 | 111 | ## File-based project format: 112 | *.ipr 113 | *.iws 114 | 115 | ## Plugin-specific files: 116 | 117 | # IntelliJ 118 | /out/ 119 | 120 | # mpeltonen/sbt-idea plugin 121 | .idea_modules/ 122 | 123 | # JIRA plugin 124 | atlassian-ide-plugin.xml 125 | 126 | # Crashlytics plugin (for Android Studio and IntelliJ) 127 | com_crashlytics_export_strings.xml 128 | crashlytics.properties 129 | crashlytics-build.properties 130 | 131 | # SublimeText.gitignore ####################################################### 132 | 133 | # cache files for sublime text 134 | *.tmlanguage.cache 135 | *.tmPreferences.cache 136 | *.stTheme.cache 137 | 138 | # workspace files are user-specific 139 | *.sublime-workspace 140 | 141 | # project files should be checked into the repository, unless a significant 142 | # proportion of contributors will probably not be using SublimeText 143 | # *.sublime-project 144 | 145 | # sftp configuration file 146 | sftp-config.json 147 | 148 | # Vim.gitignore ############################################################### 149 | 150 | [._]*.s[a-w][a-z] 151 | [._]s[a-w][a-z] 152 | *.un~ 153 | Session.vim 154 | .netrwhist 155 | *~ 156 | 157 | # Emacs.gitignore ############################################################# 158 | 159 | # -*- mode: gitignore; -*- 160 | *~ 161 | \#*\# 162 | /.emacs.desktop 163 | /.emacs.desktop.lock 164 | *.elc 165 | auto-save-list 166 | tramp 167 | .\#* 168 | 169 | # Org-mode 170 | .org-id-locations 171 | *_archive 172 | 173 | # flymake-mode 174 | *_flymake.* 175 | 176 | # eshell files 177 | /eshell/history 178 | /eshell/lastdir 179 | 180 | # elpa packages 181 | /elpa/ 182 | 183 | # reftex files 184 | *.rel 185 | 186 | # AUCTeX auto folder 187 | /auto/ 188 | 189 | # cask packages 190 | .cask/ 191 | 192 | # Python ignore ############################################################### 193 | 194 | *.py[cod] 195 | 196 | # C extensions 197 | *.so 198 | 199 | # Packages 200 | *.egg 201 | *.egg-info 202 | dist 203 | build 204 | eggs 205 | parts 206 | bin 207 | var 208 | sdist 209 | develop-eggs 210 | .installed.cfg 211 | lib 212 | lib64 213 | __pycache__ 214 | 215 | # Installer logs 216 | pip-log.txt 217 | 218 | # Unit test / coverage reports 219 | .coverage 220 | .tox 221 | nosetests.xml 222 | 223 | # OSX.gitignore ############################################################### 224 | 225 | .DS_Store 226 | .AppleDouble 227 | .LSOverride 228 | 229 | # Icon must end with two \r 230 | Icon 231 | 232 | # Thumbnails 233 | ._* 234 | 235 | # Files that might appear in the root of a volume 236 | .DocumentRevisions-V100 237 | .fseventsd 238 | .Spotlight-V100 239 | .TemporaryItems 240 | .Trashes 241 | .VolumeIcon.icns 242 | 243 | # Directories potentially created on remote AFP share 244 | .AppleDB 245 | .AppleDesktop 246 | Network Trash Folder 247 | Temporary Items 248 | .apdisk 249 | 250 | # Windows.gitignore ########################################################### 251 | 252 | # Windows image file caches 253 | Thumbs.db 254 | ehthumbs.db 255 | 256 | # Folder config file 257 | Desktop.ini 258 | 259 | # Recycle Bin used on file shares 260 | $RECYCLE.BIN/ 261 | 262 | # Windows Installer files 263 | *.cab 264 | *.msi 265 | *.msm 266 | *.msp 267 | 268 | # Windows shortcuts 269 | *.lnk 270 | 271 | # Linux.gitignore ############################################################# 272 | 273 | *~ 274 | 275 | # KDE directory preferences 276 | .directory 277 | 278 | # Linux trash folder which might appear on any partition or disk 279 | .Trash-* 280 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: java 2 | 3 | jdk: 4 | - oraclejdk7 5 | 6 | install: mvn install -DskipTests=true -Dgpg.skip=true 7 | 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # compiler 2 | 3 | Compile Java code in memory using Java 6 compiler API. 4 | 5 | [![Build Status](https://travis-ci.org/michaelliao/compiler.svg?branch=master)](https://travis-ci.org/michaelliao/compiler) 6 | 7 | ### Why compiler API? 8 | 9 | You can use compiler API to compile generated Java source code and load the compiled classes on-the-fly. 10 | 11 | For example, generate proxy classes using compiler API instead of CGLIB or Javassist. 12 | 13 | ### How to use this compiler? 14 | 15 | Step 1: add maven dependency: 16 | 17 | ``` 18 | 19 | com.itranswarp 20 | compiler 21 | 1.0 22 | 23 | ``` 24 | 25 | Step 2: compile string and load class: 26 | 27 | ``` 28 | public class Main { 29 | 30 | public static void main(String[] args) { 31 | JavaStringCompiler compiler = new JavaStringCompiler(); 32 | Map results = compiler.compile("UserProxy.java", JAVA_SOURCE_CODE); 33 | Class clazz = compiler.loadClass("on.the.fly.UserProxy", results); 34 | // try instance: 35 | User user = (User) clazz.newInstance(); 36 | } 37 | 38 | static final String JAVA_SOURCE_CODE = "/* a single java source file */ " 39 | + "package on.the.fly; " 40 | + "public class UserProxy extends test.User { " 41 | + " boolean _dirty = false; " 42 | + " public void setId(String id) { " 43 | + " super.setId(id); " 44 | + " setDirty(true); " 45 | + " } " 46 | + " public void setName(String name) { " 47 | + " super.setName(name); " 48 | + " setDirty(true); " 49 | + " } " 50 | + " public void setCreated(long created) { " 51 | + " super.setCreated(created); " 52 | + " setDirty(true); " 53 | + " } " 54 | + " public void setDirty(boolean dirty) { " 55 | + " this._dirty = dirty; " 56 | + " } " 57 | + " public boolean isDirty() { " 58 | + " return this._dirty; " 59 | + " } " 60 | + "} "; 61 | } 62 | ``` 63 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4.0.0 4 | 5 | com.itranswarp 6 | compiler 7 | 1.0 8 | jar 9 | 10 | compiler 11 | Compile Java code in memory using Java 7 compiler API 12 | https://github.com/michaelliao/compiler 13 | 14 | 15 | 16 | The Apache License, Version 2.0 17 | http://www.apache.org/licenses/LICENSE-2.0.txt 18 | 19 | 20 | 21 | 22 | 23 | Michael Liao 24 | askxuefeng@gmail.com 25 | iTranswarp 26 | http://www.liaoxuefeng.com 27 | 28 | 29 | 30 | 31 | https://github.com:michaelliao/compiler.git 32 | scm:git:git@github.com:michaelliao/compiler.git 33 | scm:git:git@github.com:michaelliao/compiler.git 34 | 35 | 36 | 37 | 38 | UTF-8 39 | 40 | 1.0 41 | 42 | yyyyMMdd_HHmmss 43 | 44 | 2.1.1 45 | 4.12 46 | 47 | 48 | 49 | 50 | org.eclipse.persistence 51 | javax.persistence 52 | ${jpa.version} 53 | test 54 | 55 | 56 | junit 57 | junit 58 | ${junit.version} 59 | test 60 | 61 | 62 | 63 | 64 | 65 | 66 | org.apache.maven.plugins 67 | maven-jar-plugin 68 | 2.6 69 | 70 | 71 | 72 | jar 73 | 74 | 75 | 76 | 77 | 78 | org.apache.maven.plugins 79 | maven-compiler-plugin 80 | 3.2 81 | 82 | 1.7 83 | 1.7 84 | -verbose 85 | 86 | 87 | 88 | org.apache.maven.plugins 89 | maven-source-plugin 90 | 2.2.1 91 | 92 | 93 | attach-sources 94 | 95 | jar-no-fork 96 | 97 | 98 | 99 | 100 | 101 | org.apache.maven.plugins 102 | maven-javadoc-plugin 103 | 2.9.1 104 | 105 | 106 | attach-javadocs 107 | 108 | jar 109 | 110 | 111 | 112 | 113 | 114 | org.apache.maven.plugins 115 | maven-gpg-plugin 116 | 1.5 117 | 118 | 119 | sign-artifacts 120 | verify 121 | 122 | sign 123 | 124 | 125 | 126 | 127 | 128 | org.sonatype.plugins 129 | nexus-staging-maven-plugin 130 | 1.6.3 131 | true 132 | 133 | ossrh 134 | https://oss.sonatype.org/ 135 | true 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | ossrh 144 | https://oss.sonatype.org/content/repositories/snapshots 145 | 146 | 147 | 148 | ossrh 149 | Nexus Release Repository 150 | http://oss.sonatype.org/service/local/staging/deploy/maven2/ 151 | 152 | 153 | 154 | -------------------------------------------------------------------------------- /src/main/java/com/itranswarp/compiler/JavaStringCompiler.java: -------------------------------------------------------------------------------- 1 | package com.itranswarp.compiler; 2 | 3 | import java.io.IOException; 4 | import java.util.Arrays; 5 | import java.util.Map; 6 | 7 | import javax.tools.JavaCompiler; 8 | import javax.tools.JavaFileObject; 9 | import javax.tools.StandardJavaFileManager; 10 | import javax.tools.ToolProvider; 11 | import javax.tools.JavaCompiler.CompilationTask; 12 | 13 | /** 14 | * In-memory compile Java source code as String. 15 | * 16 | * @author michael 17 | */ 18 | public class JavaStringCompiler { 19 | 20 | JavaCompiler compiler; 21 | StandardJavaFileManager stdManager; 22 | 23 | public JavaStringCompiler() { 24 | this.compiler = ToolProvider.getSystemJavaCompiler(); 25 | this.stdManager = compiler.getStandardFileManager(null, null, null); 26 | } 27 | 28 | /** 29 | * Compile a Java source file in memory. 30 | * 31 | * @param fileName 32 | * Java file name, e.g. "Test.java" 33 | * @param source 34 | * The source code as String. 35 | * @return The compiled results as Map that contains class name as key, 36 | * class binary as value. 37 | * @throws IOException 38 | * If compile error. 39 | */ 40 | public Map compile(String fileName, String source) throws IOException { 41 | try (MemoryJavaFileManager manager = new MemoryJavaFileManager(stdManager)) { 42 | JavaFileObject javaFileObject = manager.makeStringSource(fileName, source); 43 | CompilationTask task = compiler.getTask(null, manager, null, null, null, Arrays.asList(javaFileObject)); 44 | Boolean result = task.call(); 45 | if (result == null || !result.booleanValue()) { 46 | throw new RuntimeException("Compilation failed."); 47 | } 48 | return manager.getClassBytes(); 49 | } 50 | } 51 | 52 | /** 53 | * Load class from compiled classes. 54 | * 55 | * @param name 56 | * Full class name. 57 | * @param classBytes 58 | * Compiled results as a Map. 59 | * @return The Class instance. 60 | * @throws ClassNotFoundException 61 | * If class not found. 62 | * @throws IOException 63 | * If load error. 64 | */ 65 | public Class loadClass(String name, Map classBytes) throws ClassNotFoundException, IOException { 66 | try (MemoryClassLoader classLoader = new MemoryClassLoader(classBytes)) { 67 | return classLoader.loadClass(name); 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/main/java/com/itranswarp/compiler/MemoryClassLoader.java: -------------------------------------------------------------------------------- 1 | package com.itranswarp.compiler; 2 | 3 | import java.net.URL; 4 | import java.net.URLClassLoader; 5 | import java.util.HashMap; 6 | import java.util.Map; 7 | 8 | /** 9 | * Load class from byte[] which is compiled in memory. 10 | * 11 | * @author michael 12 | */ 13 | class MemoryClassLoader extends URLClassLoader { 14 | 15 | // class name to class bytes: 16 | Map classBytes = new HashMap(); 17 | 18 | public MemoryClassLoader(Map classBytes) { 19 | super(new URL[0], MemoryClassLoader.class.getClassLoader()); 20 | this.classBytes.putAll(classBytes); 21 | } 22 | 23 | @Override 24 | protected Class findClass(String name) throws ClassNotFoundException { 25 | byte[] buf = classBytes.get(name); 26 | if (buf == null) { 27 | return super.findClass(name); 28 | } 29 | classBytes.remove(name); 30 | return defineClass(name, buf, 0, buf.length); 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/com/itranswarp/compiler/MemoryJavaFileManager.java: -------------------------------------------------------------------------------- 1 | package com.itranswarp.compiler; 2 | 3 | import java.io.ByteArrayOutputStream; 4 | import java.io.FilterOutputStream; 5 | import java.io.IOException; 6 | import java.io.OutputStream; 7 | import java.net.URI; 8 | import java.nio.CharBuffer; 9 | import java.util.HashMap; 10 | import java.util.Map; 11 | 12 | import javax.tools.FileObject; 13 | import javax.tools.ForwardingJavaFileManager; 14 | import javax.tools.JavaFileManager; 15 | import javax.tools.JavaFileObject; 16 | import javax.tools.JavaFileObject.Kind; 17 | import javax.tools.SimpleJavaFileObject; 18 | 19 | /** 20 | * In-memory java file manager. 21 | * 22 | * @author michael 23 | */ 24 | class MemoryJavaFileManager extends ForwardingJavaFileManager { 25 | 26 | // compiled classes in bytes: 27 | final Map classBytes = new HashMap(); 28 | 29 | MemoryJavaFileManager(JavaFileManager fileManager) { 30 | super(fileManager); 31 | } 32 | 33 | public Map getClassBytes() { 34 | return new HashMap(this.classBytes); 35 | } 36 | 37 | @Override 38 | public void flush() throws IOException { 39 | } 40 | 41 | @Override 42 | public void close() throws IOException { 43 | classBytes.clear(); 44 | } 45 | 46 | @Override 47 | public JavaFileObject getJavaFileForOutput(JavaFileManager.Location location, String className, Kind kind, 48 | FileObject sibling) throws IOException { 49 | if (kind == Kind.CLASS) { 50 | return new MemoryOutputJavaFileObject(className); 51 | } else { 52 | return super.getJavaFileForOutput(location, className, kind, sibling); 53 | } 54 | } 55 | 56 | JavaFileObject makeStringSource(String name, String code) { 57 | return new MemoryInputJavaFileObject(name, code); 58 | } 59 | 60 | static class MemoryInputJavaFileObject extends SimpleJavaFileObject { 61 | 62 | final String code; 63 | 64 | MemoryInputJavaFileObject(String name, String code) { 65 | super(URI.create("string:///" + name), Kind.SOURCE); 66 | this.code = code; 67 | } 68 | 69 | @Override 70 | public CharBuffer getCharContent(boolean ignoreEncodingErrors) { 71 | return CharBuffer.wrap(code); 72 | } 73 | } 74 | 75 | class MemoryOutputJavaFileObject extends SimpleJavaFileObject { 76 | final String name; 77 | 78 | MemoryOutputJavaFileObject(String name) { 79 | super(URI.create("string:///" + name), Kind.CLASS); 80 | this.name = name; 81 | } 82 | 83 | @Override 84 | public OutputStream openOutputStream() { 85 | return new FilterOutputStream(new ByteArrayOutputStream()) { 86 | @Override 87 | public void close() throws IOException { 88 | out.close(); 89 | ByteArrayOutputStream bos = (ByteArrayOutputStream) out; 90 | classBytes.put(name, bos.toByteArray()); 91 | } 92 | }; 93 | } 94 | 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /src/test/java/com/itranswarp/compiler/JavaStringCompilerTest.java: -------------------------------------------------------------------------------- 1 | package com.itranswarp.compiler; 2 | 3 | import static org.junit.Assert.*; 4 | 5 | import java.lang.reflect.Method; 6 | import java.util.Map; 7 | 8 | import org.junit.Before; 9 | import org.junit.Test; 10 | 11 | import com.itranswarp.on.the.fly.BeanProxy; 12 | import com.itranswarp.on.the.fly.User; 13 | 14 | public class JavaStringCompilerTest { 15 | 16 | JavaStringCompiler compiler; 17 | 18 | @Before 19 | public void setUp() throws Exception { 20 | compiler = new JavaStringCompiler(); 21 | } 22 | 23 | static final String SINGLE_JAVA = "/* a single java class to one file */ " 24 | + "package on.the.fly; " 25 | + "import com.itranswarp.on.the.fly.*; " 26 | + "public class UserProxy extends User implements BeanProxy { " 27 | + " boolean _dirty = false; " 28 | + " public void setId(String id) { " 29 | + " super.setId(id); " 30 | + " setDirty(true); " 31 | + " } " 32 | + " public void setName(String name) { " 33 | + " super.setName(name); " 34 | + " setDirty(true); " 35 | + " } " 36 | + " public void setCreated(long created) { " 37 | + " super.setCreated(created); " 38 | + " setDirty(true); " 39 | + " } " 40 | + " public void setDirty(boolean dirty) { " 41 | + " this._dirty = dirty; " 42 | + " } " 43 | + " public boolean isDirty() { " 44 | + " return this._dirty; " 45 | + " } " 46 | + "} "; 47 | 48 | @Test 49 | public void testCompileSingleClass() throws Exception { 50 | Map results = compiler.compile("UserProxy.java", SINGLE_JAVA); 51 | assertEquals(1, results.size()); 52 | assertTrue(results.containsKey("on.the.fly.UserProxy")); 53 | Class clazz = compiler.loadClass("on.the.fly.UserProxy", results); 54 | // get method: 55 | Method setId = clazz.getMethod("setId", String.class); 56 | Method setName = clazz.getMethod("setName", String.class); 57 | Method setCreated = clazz.getMethod("setCreated", long.class); 58 | // try instance: 59 | Object obj = clazz.newInstance(); 60 | // get as proxy: 61 | BeanProxy proxy = (BeanProxy) obj; 62 | assertFalse(proxy.isDirty()); 63 | // set: 64 | setId.invoke(obj, "A-123"); 65 | setName.invoke(obj, "Fly"); 66 | setCreated.invoke(obj, 123000999); 67 | // get as user: 68 | User user = (User) obj; 69 | assertEquals("A-123", user.getId()); 70 | assertEquals("Fly", user.getName()); 71 | assertEquals(123000999, user.getCreated()); 72 | assertTrue(proxy.isDirty()); 73 | } 74 | 75 | static final String MULTIPLE_JAVA = "/* a single class to many files */ " 76 | + "package on.the.fly; " 77 | + "import java.util.*; " 78 | + "public class Multiple { " 79 | + " List list = new ArrayList(); " 80 | + " public void add(String name) { " 81 | + " Bird bird = new Bird(); " 82 | + " bird.name = name; " 83 | + " this.list.add(bird); " 84 | + " } " 85 | + " public Bird getFirstBird() { " 86 | + " return this.list.get(0); " 87 | + " } " 88 | + " public static class StaticBird { " 89 | + " public int weight = 100; " 90 | + " } " 91 | + " class NestedBird { " 92 | + " NestedBird() { " 93 | + " System.out.println(list.size() + \" birds...\"); " 94 | + " } " 95 | + " } " 96 | + "} " 97 | + "/* package level */ " 98 | + "class Bird { " 99 | + " String name = null; " 100 | + "} "; 101 | 102 | @Test 103 | public void testCompileMultipleClasses() throws Exception { 104 | Map results = compiler.compile("Multiple.java", MULTIPLE_JAVA); 105 | assertEquals(4, results.size()); 106 | assertTrue(results.containsKey("on.the.fly.Multiple")); 107 | assertTrue(results.containsKey("on.the.fly.Multiple$StaticBird")); 108 | assertTrue(results.containsKey("on.the.fly.Multiple$NestedBird")); 109 | assertTrue(results.containsKey("on.the.fly.Bird")); 110 | Class clzMul = compiler.loadClass("on.the.fly.Multiple", results); 111 | // try instance: 112 | Object obj = clzMul.newInstance(); 113 | assertNotNull(obj); 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /src/test/java/com/itranswarp/on/the/fly/BeanProxy.java: -------------------------------------------------------------------------------- 1 | package com.itranswarp.on.the.fly; 2 | 3 | /** 4 | * Sample interface. 5 | * 6 | * @author michael 7 | */ 8 | public interface BeanProxy { 9 | 10 | void setDirty(boolean dirty); 11 | 12 | boolean isDirty(); 13 | 14 | } 15 | -------------------------------------------------------------------------------- /src/test/java/com/itranswarp/on/the/fly/User.java: -------------------------------------------------------------------------------- 1 | package com.itranswarp.on.the.fly; 2 | 3 | /** 4 | * Sample class as JavaBean. 5 | * 6 | * @author michael 7 | */ 8 | public class User { 9 | 10 | private String id; 11 | private String name; 12 | private long created; 13 | 14 | public String getId() { 15 | return id; 16 | } 17 | 18 | public void setId(String id) { 19 | this.id = id; 20 | } 21 | 22 | public String getName() { 23 | return name; 24 | } 25 | 26 | public void setName(String name) { 27 | this.name = name; 28 | } 29 | 30 | public long getCreated() { 31 | return created; 32 | } 33 | 34 | public void setCreated(long created) { 35 | this.created = created; 36 | } 37 | 38 | } 39 | --------------------------------------------------------------------------------