Bukkit-API  1.7.9-R0.2
The inofficial Bukkit-API
JavaPlugin.java
1 package org.bukkit.plugin.java;
2 
3 import java.io.File;
4 import java.io.FileOutputStream;
5 import java.io.IOException;
6 import java.io.InputStream;
7 import java.io.InputStreamReader;
8 import java.io.OutputStream;
9 import java.io.Reader;
10 import java.net.URL;
11 import java.net.URLConnection;
12 import java.nio.charset.Charset;
13 import java.util.ArrayList;
14 import java.util.List;
15 import java.util.logging.Level;
16 import java.util.logging.Logger;
17 
18 import org.apache.commons.lang.Validate;
19 import org.bukkit.Server;
21 import org.bukkit.command.Command;
34 
35 import com.avaje.ebean.EbeanServer;
36 import com.avaje.ebean.EbeanServerFactory;
37 import com.avaje.ebean.config.DataSourceConfig;
38 import com.avaje.ebean.config.ServerConfig;
39 import com.avaje.ebeaninternal.api.SpiEbeanServer;
40 import com.avaje.ebeaninternal.server.ddl.DdlGenerator;
41 import com.google.common.base.Charsets;
42 import com.google.common.io.ByteStreams;
43 
44 /**
45  * Represents a Java plugin
46  */
47 public abstract class JavaPlugin extends PluginBase {
48  private boolean isEnabled = false;
49  private PluginLoader loader = null;
50  private Server server = null;
51  private File file = null;
52  private PluginDescriptionFile description = null;
53  private File dataFolder = null;
54  private ClassLoader classLoader = null;
55  private boolean naggable = true;
56  private EbeanServer ebean = null;
57  private FileConfiguration newConfig = null;
58  private File configFile = null;
59  private PluginLogger logger = null;
60 
61  public JavaPlugin() {
62  final ClassLoader classLoader = this.getClass().getClassLoader();
63  if (!(classLoader instanceof PluginClassLoader)) {
64  throw new IllegalStateException("JavaPlugin requires " + PluginClassLoader.class.getName());
65  }
66  ((PluginClassLoader) classLoader).initialize(this);
67  }
68 
69  /**
70  * @deprecated This method is intended for unit testing purposes when the
71  * other {@linkplain #JavaPlugin(JavaPluginLoader,
72  * PluginDescriptionFile, File, File) constructor} cannot be used.
73  * <p>
74  * Its existence may be temporary.
75  */
76  @Deprecated
77  protected JavaPlugin(final PluginLoader loader, final Server server, final PluginDescriptionFile description, final File dataFolder, final File file) {
78  final ClassLoader classLoader = this.getClass().getClassLoader();
79  if (classLoader instanceof PluginClassLoader) {
80  throw new IllegalStateException("Cannot use initialization constructor at runtime");
81  }
82  init(loader, server, description, dataFolder, file, classLoader);
83  }
84 
85  protected JavaPlugin(final JavaPluginLoader loader, final PluginDescriptionFile description, final File dataFolder, final File file) {
86  final ClassLoader classLoader = this.getClass().getClassLoader();
87  if (classLoader instanceof PluginClassLoader) {
88  throw new IllegalStateException("Cannot use initialization constructor at runtime");
89  }
90  init(loader, loader.server, description, dataFolder, file, classLoader);
91  }
92 
93  /**
94  * Returns the folder that the plugin data's files are located in. The
95  * folder may not yet exist.
96  *
97  * @return The folder.
98  */
99  @Override
100  public final File getDataFolder() {
101  return dataFolder;
102  }
103 
104  /**
105  * Gets the associated PluginLoader responsible for this plugin
106  *
107  * @return PluginLoader that controls this plugin
108  */
109  @Override
110  public final PluginLoader getPluginLoader() {
111  return loader;
112  }
113 
114  /**
115  * Returns the Server instance currently running this plugin
116  *
117  * @return Server running this plugin
118  */
119  @Override
120  public final Server getServer() {
121  return server;
122  }
123 
124  /**
125  * Returns a value indicating whether or not this plugin is currently
126  * enabled
127  *
128  * @return true if this plugin is enabled, otherwise false
129  */
130  @Override
131  public final boolean isEnabled() {
132  return isEnabled;
133  }
134 
135  /**
136  * Returns the file which contains this plugin
137  *
138  * @return File containing this plugin
139  */
140  protected File getFile() {
141  return file;
142  }
143 
144  /**
145  * Returns the plugin.yaml file containing the details for this plugin
146  *
147  * @return Contents of the plugin.yaml file
148  */
149  @Override
151  return description;
152  }
153 
154  @Override
156  if (newConfig == null) {
157  reloadConfig();
158  }
159  return newConfig;
160  }
161 
162  /**
163  * Provides a reader for a text file located inside the jar. The behavior
164  * of this method adheres to {@link PluginAwareness.Flags#UTF8}, or if not
165  * defined, uses UTF8 if {@link FileConfiguration#UTF8_OVERRIDE} is
166  * specified, or system default otherwise.
167  *
168  * @param file the filename of the resource to load
169  * @return null if {@link #getResource(String)} returns null
170  * @throws IllegalArgumentException if file is null
171  * @see ClassLoader#getResourceAsStream(String)
172  */
173  @SuppressWarnings("deprecation")
174  protected final Reader getTextResource(String file) {
175  final InputStream in = getResource(file);
176 
177  return in == null ? null : new InputStreamReader(in, isStrictlyUTF8() || FileConfiguration.UTF8_OVERRIDE ? Charsets.UTF_8 : Charset.defaultCharset());
178  }
179 
180  @SuppressWarnings("deprecation")
181  @Override
182  public void reloadConfig() {
183  newConfig = YamlConfiguration.loadConfiguration(configFile);
184 
185  final InputStream defConfigStream = getResource("config.yml");
186  if (defConfigStream == null) {
187  return;
188  }
189 
190  final YamlConfiguration defConfig;
191  if (isStrictlyUTF8() || FileConfiguration.UTF8_OVERRIDE) {
192  defConfig = YamlConfiguration.loadConfiguration(new InputStreamReader(defConfigStream, Charsets.UTF_8));
193  } else {
194  final byte[] contents;
195  defConfig = new YamlConfiguration();
196  try {
197  contents = ByteStreams.toByteArray(defConfigStream);
198  } catch (final IOException e) {
199  getLogger().log(Level.SEVERE, "Unexpected failure reading config.yml", e);
200  return;
201  }
202 
203  final String text = new String(contents, Charset.defaultCharset());
204  if (!text.equals(new String(contents, Charsets.UTF_8))) {
205  getLogger().warning("Default system encoding may have misread config.yml from plugin jar");
206  }
207 
208  try {
209  defConfig.loadFromString(text);
210  } catch (final InvalidConfigurationException e) {
211  getLogger().log(Level.SEVERE, "Cannot load configuration from jar", e);
212  }
213  }
214 
215  newConfig.setDefaults(defConfig);
216  }
217 
218  private boolean isStrictlyUTF8() {
220  }
221 
222  @Override
223  public void saveConfig() {
224  try {
225  getConfig().save(configFile);
226  } catch (IOException ex) {
227  logger.log(Level.SEVERE, "Could not save config to " + configFile, ex);
228  }
229  }
230 
231  @Override
232  public void saveDefaultConfig() {
233  if (!configFile.exists()) {
234  saveResource("config.yml", false);
235  }
236  }
237 
238  @Override
239  public void saveResource(String resourcePath, boolean replace) {
240  if (resourcePath == null || resourcePath.equals("")) {
241  throw new IllegalArgumentException("ResourcePath cannot be null or empty");
242  }
243 
244  resourcePath = resourcePath.replace('\\', '/');
245  InputStream in = getResource(resourcePath);
246  if (in == null) {
247  throw new IllegalArgumentException("The embedded resource '" + resourcePath + "' cannot be found in " + file);
248  }
249 
250  File outFile = new File(dataFolder, resourcePath);
251  int lastIndex = resourcePath.lastIndexOf('/');
252  File outDir = new File(dataFolder, resourcePath.substring(0, lastIndex >= 0 ? lastIndex : 0));
253 
254  if (!outDir.exists()) {
255  outDir.mkdirs();
256  }
257 
258  try {
259  if (!outFile.exists() || replace) {
260  OutputStream out = new FileOutputStream(outFile);
261  byte[] buf = new byte[1024];
262  int len;
263  while ((len = in.read(buf)) > 0) {
264  out.write(buf, 0, len);
265  }
266  out.close();
267  in.close();
268  } else {
269  logger.log(Level.WARNING, "Could not save " + outFile.getName() + " to " + outFile + " because " + outFile.getName() + " already exists.");
270  }
271  } catch (IOException ex) {
272  logger.log(Level.SEVERE, "Could not save " + outFile.getName() + " to " + outFile, ex);
273  }
274  }
275 
276  @Override
277  public InputStream getResource(String filename) {
278  if (filename == null) {
279  throw new IllegalArgumentException("Filename cannot be null");
280  }
281 
282  try {
283  URL url = getClassLoader().getResource(filename);
284 
285  if (url == null) {
286  return null;
287  }
288 
289  URLConnection connection = url.openConnection();
290  connection.setUseCaches(false);
291  return connection.getInputStream();
292  } catch (IOException ex) {
293  return null;
294  }
295  }
296 
297  /**
298  * Returns the ClassLoader which holds this plugin
299  *
300  * @return ClassLoader holding this plugin
301  */
302  protected final ClassLoader getClassLoader() {
303  return classLoader;
304  }
305 
306  /**
307  * Sets the enabled state of this plugin
308  *
309  * @param enabled true if enabled, otherwise false
310  */
311  protected final void setEnabled(final boolean enabled) {
312  if (isEnabled != enabled) {
313  isEnabled = enabled;
314 
315  if (isEnabled) {
316  onEnable();
317  } else {
318  onDisable();
319  }
320  }
321  }
322 
323  /**
324  * @deprecated This method is legacy and will be removed - it must be
325  * replaced by the specially provided constructor(s).
326  */
327  @Deprecated
328  protected final void initialize(PluginLoader loader, Server server, PluginDescriptionFile description, File dataFolder, File file, ClassLoader classLoader) {
329  if (server.getWarningState() == WarningState.OFF) {
330  return;
331  }
332  getLogger().log(Level.WARNING, getClass().getName() + " is already initialized", server.getWarningState() == WarningState.DEFAULT ? null : new AuthorNagException("Explicit initialization"));
333  }
334 
335  final void init(PluginLoader loader, Server server, PluginDescriptionFile description, File dataFolder, File file, ClassLoader classLoader) {
336  this.loader = loader;
337  this.server = server;
338  this.file = file;
339  this.description = description;
340  this.dataFolder = dataFolder;
341  this.classLoader = classLoader;
342  this.configFile = new File(dataFolder, "config.yml");
343  this.logger = new PluginLogger(this);
344 
345  if (description.isDatabaseEnabled()) {
346  ServerConfig db = new ServerConfig();
347 
348  db.setDefaultServer(false);
349  db.setRegister(false);
350  db.setClasses(getDatabaseClasses());
351  db.setName(description.getName());
352  server.configureDbConfig(db);
353 
354  DataSourceConfig ds = db.getDataSourceConfig();
355 
356  ds.setUrl(replaceDatabaseString(ds.getUrl()));
357  dataFolder.mkdirs();
358 
359  ClassLoader previous = Thread.currentThread().getContextClassLoader();
360 
361  Thread.currentThread().setContextClassLoader(classLoader);
362  ebean = EbeanServerFactory.create(db);
363  Thread.currentThread().setContextClassLoader(previous);
364  }
365  }
366 
367  /**
368  * Provides a list of all classes that should be persisted in the database
369  *
370  * @return List of Classes that are Ebeans
371  */
372  public List<Class<?>> getDatabaseClasses() {
373  return new ArrayList<Class<?>>();
374  }
375 
376  private String replaceDatabaseString(String input) {
377  input = input.replaceAll("\\{DIR\\}", dataFolder.getPath().replaceAll("\\\\", "/") + "/");
378  input = input.replaceAll("\\{NAME\\}", description.getName().replaceAll("[^\\w_-]", ""));
379  return input;
380  }
381 
382  /**
383  * Gets the initialization status of this plugin
384  *
385  * @return true if this plugin is initialized, otherwise false
386  * @deprecated This method cannot return false, as {@link
387  * JavaPlugin} is now initialized in the constructor.
388  */
389  @Deprecated
390  public final boolean isInitialized() {
391  return true;
392  }
393 
394  /**
395  * {@inheritDoc}
396  */
397  @Override
398  public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
399  return false;
400  }
401 
402  /**
403  * {@inheritDoc}
404  */
405  @Override
406  public List<String> onTabComplete(CommandSender sender, Command command, String alias, String[] args) {
407  return null;
408  }
409 
410  /**
411  * Gets the command with the given name, specific to this plugin. Commands
412  * need to be registered in the {@link PluginDescriptionFile#getCommands()
413  * PluginDescriptionFile} to exist at runtime.
414  *
415  * @param name name or alias of the command
416  * @return the plugin command if found, otherwise null
417  */
418  public PluginCommand getCommand(String name) {
419  String alias = name.toLowerCase();
420  PluginCommand command = getServer().getPluginCommand(alias);
421 
422  if (command == null || command.getPlugin() != this) {
423  command = getServer().getPluginCommand(description.getName().toLowerCase() + ":" + alias);
424  }
425 
426  if (command != null && command.getPlugin() == this) {
427  return command;
428  } else {
429  return null;
430  }
431  }
432 
433  @Override
434  public void onLoad() {}
435 
436  @Override
437  public void onDisable() {}
438 
439  @Override
440  public void onEnable() {}
441 
442  @Override
443  public ChunkGenerator getDefaultWorldGenerator(String worldName, String id) {
444  return null;
445  }
446 
447  @Override
448  public final boolean isNaggable() {
449  return naggable;
450  }
451 
452  @Override
453  public final void setNaggable(boolean canNag) {
454  this.naggable = canNag;
455  }
456 
457  @Override
458  public EbeanServer getDatabase() {
459  return ebean;
460  }
461 
462  protected void installDDL() {
463  SpiEbeanServer serv = (SpiEbeanServer) getDatabase();
464  DdlGenerator gen = serv.getDdlGenerator();
465 
466  gen.runScript(false, gen.generateCreateDdl());
467  }
468 
469  protected void removeDDL() {
470  SpiEbeanServer serv = (SpiEbeanServer) getDatabase();
471  DdlGenerator gen = serv.getDdlGenerator();
472 
473  gen.runScript(true, gen.generateDropDdl());
474  }
475 
476  @Override
477  public final Logger getLogger() {
478  return logger;
479  }
480 
481  @Override
482  public String toString() {
483  return description.getFullName();
484  }
485 
486  /**
487  * This method provides fast access to the plugin that has {@link
488  * #getProvidingPlugin(Class) provided} the given plugin class, which is
489  * usually the plugin that implemented it.
490  * <p>
491  * An exception to this would be if plugin's jar that contained the class
492  * does not extend the class, where the intended plugin would have
493  * resided in a different jar / classloader.
494  *
495  * @param clazz the class desired
496  * @return the plugin that provides and implements said class
497  * @throws IllegalArgumentException if clazz is null
498  * @throws IllegalArgumentException if clazz does not extend {@link
499  * JavaPlugin}
500  * @throws IllegalStateException if clazz was not provided by a plugin,
501  * for example, if called with
502  * <code>JavaPlugin.getPlugin(JavaPlugin.class)</code>
503  * @throws IllegalStateException if called from the static initializer for
504  * given JavaPlugin
505  * @throws ClassCastException if plugin that provided the class does not
506  * extend the class
507  */
508  public static <T extends JavaPlugin> T getPlugin(Class<T> clazz) {
509  Validate.notNull(clazz, "Null class cannot have a plugin");
510  if (!JavaPlugin.class.isAssignableFrom(clazz)) {
511  throw new IllegalArgumentException(clazz + " does not extend " + JavaPlugin.class);
512  }
513  final ClassLoader cl = clazz.getClassLoader();
514  if (!(cl instanceof PluginClassLoader)) {
515  throw new IllegalArgumentException(clazz + " is not initialized by " + PluginClassLoader.class);
516  }
517  JavaPlugin plugin = ((PluginClassLoader) cl).plugin;
518  if (plugin == null) {
519  throw new IllegalStateException("Cannot get plugin for " + clazz + " from a static initializer");
520  }
521  return clazz.cast(plugin);
522  }
523 
524  /**
525  * This method provides fast access to the plugin that has provided the
526  * given class.
527  *
528  * @throws IllegalArgumentException if the class is not provided by a
529  * JavaPlugin
530  * @throws IllegalArgumentException if class is null
531  * @throws IllegalStateException if called from the static initializer for
532  * given JavaPlugin
533  */
534  public static JavaPlugin getProvidingPlugin(Class<?> clazz) {
535  Validate.notNull(clazz, "Null class cannot have a plugin");
536  final ClassLoader cl = clazz.getClassLoader();
537  if (!(cl instanceof PluginClassLoader)) {
538  throw new IllegalArgumentException(clazz + " is not provided by " + PluginClassLoader.class);
539  }
540  JavaPlugin plugin = ((PluginClassLoader) cl).plugin;
541  if (plugin == null) {
542  throw new IllegalStateException("Cannot get plugin for " + clazz + " from a static initializer");
543  }
544  return plugin;
545  }
546 }
List< Class<?> > getDatabaseClasses()
final void setEnabled(final boolean enabled)
ChunkGenerator getDefaultWorldGenerator(String worldName, String id)
PluginCommand getPluginCommand(String name)
final void setNaggable(boolean canNag)
JavaPlugin(final PluginLoader loader, final Server server, final PluginDescriptionFile description, final File dataFolder, final File file)
Definition: JavaPlugin.java:77
PluginCommand getCommand(String name)
void saveResource(String resourcePath, boolean replace)
static JavaPlugin getProvidingPlugin(Class<?> clazz)
List< String > onTabComplete(CommandSender sender, Command command, String alias, String[] args)
final Reader getTextResource(String file)
final ClassLoader getClassLoader()
final PluginLoader getPluginLoader()
final PluginDescriptionFile getDescription()
static< TextendsJavaPlugin > T getPlugin(Class< T > clazz)
boolean onCommand(CommandSender sender, Command command, String label, String[] args)
void configureDbConfig(ServerConfig config)
WarningState getWarningState()
InputStream getResource(String filename)
final void initialize(PluginLoader loader, Server server, PluginDescriptionFile description, File dataFolder, File file, ClassLoader classLoader)
static YamlConfiguration loadConfiguration(File file)