001    package org.bukkit.plugin.java;
002    
003    import java.io.File;
004    import java.io.FileOutputStream;
005    import java.io.IOException;
006    import java.io.InputStream;
007    import java.io.OutputStream;
008    import java.net.URL;
009    import java.net.URLConnection;
010    import java.util.ArrayList;
011    import java.util.List;
012    import java.util.logging.Level;
013    import java.util.logging.Logger;
014    
015    import org.bukkit.Server;
016    import org.bukkit.command.Command;
017    import org.bukkit.command.CommandSender;
018    import org.bukkit.command.PluginCommand;
019    import org.bukkit.configuration.file.FileConfiguration;
020    import org.bukkit.configuration.file.YamlConfiguration;
021    import org.bukkit.generator.ChunkGenerator;
022    import org.bukkit.plugin.PluginBase;
023    import org.bukkit.plugin.PluginDescriptionFile;
024    import org.bukkit.plugin.PluginLoader;
025    import org.bukkit.plugin.PluginLogger;
026    
027    import com.avaje.ebean.EbeanServer;
028    import com.avaje.ebean.EbeanServerFactory;
029    import com.avaje.ebean.config.DataSourceConfig;
030    import com.avaje.ebean.config.ServerConfig;
031    import com.avaje.ebeaninternal.api.SpiEbeanServer;
032    import com.avaje.ebeaninternal.server.ddl.DdlGenerator;
033    
034    /**
035     * Represents a Java plugin
036     */
037    public abstract class JavaPlugin extends PluginBase {
038        private boolean isEnabled = false;
039        private boolean initialized = false;
040        private PluginLoader loader = null;
041        private Server server = null;
042        private File file = null;
043        private PluginDescriptionFile description = null;
044        private File dataFolder = null;
045        private ClassLoader classLoader = null;
046        private boolean naggable = true;
047        private EbeanServer ebean = null;
048        private FileConfiguration newConfig = null;
049        private File configFile = null;
050        private PluginLogger logger = null;
051    
052        public JavaPlugin() {}
053    
054        /**
055         * Returns the folder that the plugin data's files are located in. The
056         * folder may not yet exist.
057         *
058         * @return The folder.
059         */
060        public final File getDataFolder() {
061            return dataFolder;
062        }
063    
064        /**
065         * Gets the associated PluginLoader responsible for this plugin
066         *
067         * @return PluginLoader that controls this plugin
068         */
069        public final PluginLoader getPluginLoader() {
070            return loader;
071        }
072    
073        /**
074         * Returns the Server instance currently running this plugin
075         *
076         * @return Server running this plugin
077         */
078        public final Server getServer() {
079            return server;
080        }
081    
082        /**
083         * Returns a value indicating whether or not this plugin is currently enabled
084         *
085         * @return true if this plugin is enabled, otherwise false
086         */
087        public final boolean isEnabled() {
088            return isEnabled;
089        }
090    
091        /**
092         * Returns the file which contains this plugin
093         *
094         * @return File containing this plugin
095         */
096        protected File getFile() {
097            return file;
098        }
099    
100        /**
101         * Returns the plugin.yaml file containing the details for this plugin
102         *
103         * @return Contents of the plugin.yaml file
104         */
105        public final PluginDescriptionFile getDescription() {
106            return description;
107        }
108    
109        public FileConfiguration getConfig() {
110            if (newConfig == null) {
111                reloadConfig();
112            }
113            return newConfig;
114        }
115    
116        public void reloadConfig() {
117            newConfig = YamlConfiguration.loadConfiguration(configFile);
118    
119            InputStream defConfigStream = getResource("config.yml");
120            if (defConfigStream != null) {
121                YamlConfiguration defConfig = YamlConfiguration.loadConfiguration(defConfigStream);
122    
123                newConfig.setDefaults(defConfig);
124            }
125        }
126    
127        public void saveConfig() {
128            try {
129                getConfig().save(configFile);
130            } catch (IOException ex) {
131                logger.log(Level.SEVERE, "Could not save config to " + configFile, ex);
132            }
133        }
134    
135        public void saveDefaultConfig() {
136            if (!configFile.exists()) {
137                saveResource("config.yml", false);
138            }
139        }
140    
141        public void saveResource(String resourcePath, boolean replace) {
142            if (resourcePath == null || resourcePath.equals("")) {
143                throw new IllegalArgumentException("ResourcePath cannot be null or empty");
144            }
145    
146            resourcePath = resourcePath.replace('\\', '/');
147            InputStream in = getResource(resourcePath);
148            if (in == null) {
149                throw new IllegalArgumentException("The embedded resource '" + resourcePath + "' cannot be found in " + file);
150            }
151    
152            File outFile = new File(dataFolder, resourcePath);
153            int lastIndex = resourcePath.lastIndexOf('/');
154            File outDir = new File(dataFolder, resourcePath.substring(0, lastIndex >= 0 ? lastIndex : 0));
155    
156            if (!outDir.exists()) {
157                outDir.mkdirs();
158            }
159    
160            try {
161                if (!outFile.exists() || replace) {
162                    OutputStream out = new FileOutputStream(outFile);
163                    byte[] buf = new byte[1024];
164                    int len;
165                    while ((len = in.read(buf)) > 0) {
166                        out.write(buf, 0, len);
167                    }
168                    out.close();
169                    in.close();
170                } else {
171                    logger.log(Level.WARNING, "Could not save " + outFile.getName() + " to " + outFile + " because " + outFile.getName() + " already exists.");
172                }
173            } catch (IOException ex) {
174                logger.log(Level.SEVERE, "Could not save " + outFile.getName() + " to " + outFile, ex);
175            }
176        }
177    
178        public InputStream getResource(String filename) {
179            if (filename == null) {
180                throw new IllegalArgumentException("Filename cannot be null");
181            }
182    
183            try {
184                URL url = getClassLoader().getResource(filename);
185    
186                if (url == null) {
187                    return null;
188                }
189    
190                URLConnection connection = url.openConnection();
191                connection.setUseCaches(false);
192                return connection.getInputStream();
193            } catch (IOException ex) {
194                return null;
195            }
196        }
197    
198        /**
199         * Returns the ClassLoader which holds this plugin
200         *
201         * @return ClassLoader holding this plugin
202         */
203        protected final ClassLoader getClassLoader() {
204            return classLoader;
205        }
206    
207        /**
208         * Sets the enabled state of this plugin
209         *
210         * @param enabled true if enabled, otherwise false
211         */
212        protected final void setEnabled(final boolean enabled) {
213            if (isEnabled != enabled) {
214                isEnabled = enabled;
215    
216                if (isEnabled) {
217                    onEnable();
218                } else {
219                    onDisable();
220                }
221            }
222        }
223    
224        /**
225         * Initializes this plugin with the given variables.
226         * <p />
227         * This method should never be called manually.
228         *
229         * @param loader PluginLoader that is responsible for this plugin
230         * @param server Server instance that is running this plugin
231         * @param description PluginDescriptionFile containing metadata on this plugin
232         * @param dataFolder Folder containing the plugin's data
233         * @param file File containing this plugin
234         * @param classLoader ClassLoader which holds this plugin
235         */
236        protected final void initialize(PluginLoader loader, Server server, PluginDescriptionFile description, File dataFolder, File file, ClassLoader classLoader) {
237            if (!initialized) {
238                this.initialized = true;
239                this.loader = loader;
240                this.server = server;
241                this.file = file;
242                this.description = description;
243                this.dataFolder = dataFolder;
244                this.classLoader = classLoader;
245                this.configFile = new File(dataFolder, "config.yml");
246                this.logger = new PluginLogger(this);
247    
248                if (description.isDatabaseEnabled()) {
249                    ServerConfig db = new ServerConfig();
250    
251                    db.setDefaultServer(false);
252                    db.setRegister(false);
253                    db.setClasses(getDatabaseClasses());
254                    db.setName(description.getName());
255                    server.configureDbConfig(db);
256    
257                    DataSourceConfig ds = db.getDataSourceConfig();
258    
259                    ds.setUrl(replaceDatabaseString(ds.getUrl()));
260                    dataFolder.mkdirs();
261    
262                    ClassLoader previous = Thread.currentThread().getContextClassLoader();
263    
264                    Thread.currentThread().setContextClassLoader(classLoader);
265                    ebean = EbeanServerFactory.create(db);
266                    Thread.currentThread().setContextClassLoader(previous);
267                }
268            }
269        }
270    
271        /**
272         * Provides a list of all classes that should be persisted in the database
273         *
274         * @return List of Classes that are Ebeans
275         */
276        public List<Class<?>> getDatabaseClasses() {
277            return new ArrayList<Class<?>>();
278        }
279    
280        private String replaceDatabaseString(String input) {
281            input = input.replaceAll("\\{DIR\\}", dataFolder.getPath().replaceAll("\\\\", "/") + "/");
282            input = input.replaceAll("\\{NAME\\}", description.getName().replaceAll("[^\\w_-]", ""));
283            return input;
284        }
285    
286        /**
287         * Gets the initialization status of this plugin
288         *
289         * @return true if this plugin is initialized, otherwise false
290         */
291        public final boolean isInitialized() {
292            return initialized;
293        }
294    
295        /**
296         * {@inheritDoc}
297         */
298        public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
299            return false;
300        }
301    
302        /**
303         * {@inheritDoc}
304         */
305        public List<String> onTabComplete(CommandSender sender, Command command, String alias, String[] args) {
306            return null;
307        }
308    
309        /**
310         * Gets the command with the given name, specific to this plugin
311         *
312         * @param name Name or alias of the command
313         * @return PluginCommand if found, otherwise null
314         */
315        public PluginCommand getCommand(String name) {
316            String alias = name.toLowerCase();
317            PluginCommand command = getServer().getPluginCommand(alias);
318    
319            if ((command != null) && (command.getPlugin() != this)) {
320                command = getServer().getPluginCommand(description.getName().toLowerCase() + ":" + alias);
321            }
322    
323            if ((command != null) && (command.getPlugin() == this)) {
324                return command;
325            } else {
326                return null;
327            }
328        }
329    
330        public void onLoad() {}
331    
332        public void onDisable() {}
333    
334        public void onEnable() {}
335    
336        public ChunkGenerator getDefaultWorldGenerator(String worldName, String id) {
337            getServer().getLogger().severe("Plugin " + description.getFullName() + " does not contain any generators that may be used in the default world!");
338            return null;
339        }
340    
341        public final boolean isNaggable() {
342            return naggable;
343        }
344    
345        public final void setNaggable(boolean canNag) {
346            this.naggable = canNag;
347        }
348    
349        public EbeanServer getDatabase() {
350            return ebean;
351        }
352    
353        protected void installDDL() {
354            SpiEbeanServer serv = (SpiEbeanServer) getDatabase();
355            DdlGenerator gen = serv.getDdlGenerator();
356    
357            gen.runScript(false, gen.generateCreateDdl());
358        }
359    
360        protected void removeDDL() {
361            SpiEbeanServer serv = (SpiEbeanServer) getDatabase();
362            DdlGenerator gen = serv.getDdlGenerator();
363    
364            gen.runScript(true, gen.generateDropDdl());
365        }
366    
367        public final Logger getLogger() {
368            return logger;
369        }
370    
371        @Override
372        public String toString() {
373            return description.getFullName();
374        }
375    }