Package ch.entwine.weblounge.common.impl.site

Source Code of ch.entwine.weblounge.common.impl.site.ModuleImpl

/*
*  Weblounge: Web Content Management System
*  Copyright (c) 2003 - 2011 The Weblounge Team
*  http://entwinemedia.com/weblounge
*
*  This program is free software; you can redistribute it and/or
*  modify it under the terms of the GNU Lesser General Public License
*  as published by the Free Software Foundation; either version 2
*  of the License, or (at your option) any later version.
*
*  This program is distributed in the hope that it will be useful,
*  but WITHOUT ANY WARRANTY; without even the implied warranty of
*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
*  GNU Lesser General Public License for more details.
*
*  You should have received a copy of the GNU Lesser General Public License
*  along with this program; if not, write to the Free Software Foundation
*  Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/

package ch.entwine.weblounge.common.impl.site;

import ch.entwine.weblounge.common.content.SearchResultItem;
import ch.entwine.weblounge.common.content.image.ImageStyle;
import ch.entwine.weblounge.common.content.page.PageletRenderer;
import ch.entwine.weblounge.common.impl.content.image.ImageStyleImpl;
import ch.entwine.weblounge.common.impl.content.page.PageletRendererImpl;
import ch.entwine.weblounge.common.impl.scheduler.QuartzJob;
import ch.entwine.weblounge.common.impl.url.WebUrlImpl;
import ch.entwine.weblounge.common.impl.util.config.OptionsHelper;
import ch.entwine.weblounge.common.impl.util.xml.XPathHelper;
import ch.entwine.weblounge.common.impl.util.xml.XPathNamespaceContext;
import ch.entwine.weblounge.common.scheduler.Job;
import ch.entwine.weblounge.common.site.Action;
import ch.entwine.weblounge.common.site.Environment;
import ch.entwine.weblounge.common.site.Module;
import ch.entwine.weblounge.common.site.ModuleException;
import ch.entwine.weblounge.common.site.ModuleListener;
import ch.entwine.weblounge.common.site.Site;
import ch.entwine.weblounge.common.site.SiteURL;
import ch.entwine.weblounge.common.url.UrlUtils;
import ch.entwine.weblounge.common.url.WebUrl;

import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;

import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathFactory;

/**
* Base implementation for a module. It is recommended that individual
* <code>Module</code> implementations extend this class.
*/
public class ModuleImpl implements Module {

  /** Logging facility */
  protected static final Logger logger = LoggerFactory.getLogger(ModuleImpl.class);

  /** Regular expression to test the validity of a module identifier */
  private static final String MODULE_IDENTIFIER_REGEX = "^[a-zA-Z0-9]+[a-zA-Z0-9-_.]*$";

  /** Xml namespace for the module */
  public static final String MODULE_XMLNS = "http://www.entwinemedia.com/weblounge/3.2/module";

  /** The module identifier */
  protected String identifier = null;

  /** The url that is used to reach module assets */
  protected WebUrl url = null;

  /** Module enabled state */
  protected boolean enabled = true;

  /** Module running state */
  private boolean running = false;

  /** Can this module be searched? */
  protected boolean searchable = false;

  /** The hosting site */
  protected Site site = null;

  /** Option handling support */
  protected OptionsHelper options = null;

  /** Localized module title */
  protected String name = null;

  /** Module pagelet renderers */
  protected Map<String, PageletRenderer> renderers = null;

  /** Module actions */
  protected Map<String, Action> actions = null;

  /** Module image styles */
  protected Map<String, ImageStyle> imagestyles = null;

  /** Module jobs */
  protected Map<String, Job> jobs = null;

  /** List of module listeners */
  protected List<ModuleListener> moduleListeners = null;

  /** The environment */
  protected Environment environment = Environment.Production;

  /**
   * Creates a new module.
   */
  public ModuleImpl() {
    renderers = new HashMap<String, PageletRenderer>();
    actions = new HashMap<String, Action>();
    imagestyles = new HashMap<String, ImageStyle>();
    jobs = new HashMap<String, Job>();
    options = new OptionsHelper();
  }

  /**
   * {@inheritDoc}
   *
   * @see ch.entwine.weblounge.common.site.Module#initialize(ch.entwine.weblounge.common.site.Environment)
   */
  public void initialize(Environment environment) {
    this.environment = environment;

    // Tell the renderers about the environment
    for (PageletRenderer renderer : renderers.values()) {
      renderer.setEnvironment(environment);
    }

    // Tell the actions about the environment
    for (Action action : actions.values()) {
      action.setEnvironment(environment);
    }

    // Tell the jobs about the environment
    for (Job job : jobs.values()) {
      job.setEnvironment(environment);
    }

    // Switch the options to the new environment
    options.setEnvironment(environment);
  }

  /**
   * {@inheritDoc}
   *
   * @see ch.entwine.weblounge.common.site.Module#setIdentifier(java.lang.String)
   */
  public void setIdentifier(String identifier) {
    if (identifier == null)
      throw new IllegalArgumentException("Module identifier must not be null");
    else if (!Pattern.matches(MODULE_IDENTIFIER_REGEX, identifier))
      throw new IllegalArgumentException("Module identifier '" + identifier + "' is malformed");
    this.identifier = identifier;
  }

  /**
   * {@inheritDoc}
   *
   * @see ch.entwine.weblounge.common.site.Module#getIdentifier()
   */
  public String getIdentifier() {
    return identifier;
  }

  /**
   * {@inheritDoc}
   *
   * @see ch.entwine.weblounge.common.site.Module#getUrl()
   */
  public WebUrl getUrl() {
    return getUrl(environment);
  }

  /**
   * {@inheritDoc}
   *
   * @see ch.entwine.weblounge.common.site.Module#getUrl(ch.entwine.weblounge.common.site.Environment)
   */
  public WebUrl getUrl(Environment environment) {
    if (url != null)
      return url;
    if (site == null)
      throw new IllegalStateException("Site has not yet been set");
    SiteURL siteURL = site.getHostname(environment);
    url = new WebUrlImpl(site, UrlUtils.concat(siteURL.toExternalForm(), "module", identifier));
    return url;
  }

  /**
   * {@inheritDoc}
   *
   * @see ch.entwine.weblounge.common.site.Module#addModuleListener(ch.entwine.weblounge.common.site.ModuleListener)
   */
  public void addModuleListener(ModuleListener listener) {
    if (moduleListeners == null)
      moduleListeners = new ArrayList<ModuleListener>();
    synchronized (moduleListeners) {
      moduleListeners.add(listener);
    }
  }

  /**
   * {@inheritDoc}
   *
   * @see ch.entwine.weblounge.common.site.Module#removeModuleListener(ch.entwine.weblounge.common.site.ModuleListener)
   */
  public void removeModuleListener(ModuleListener listener) {
    if (moduleListeners != null) {
      synchronized (moduleListeners) {
        moduleListeners.remove(listener);
      }
    }
  }

  /**
   * {@inheritDoc}
   *
   * @see ch.entwine.weblounge.common.site.Module#setSite(ch.entwine.weblounge.common.site.Site)
   */
  public void setSite(Site site) throws ModuleException {
    this.site = site;

    // Initialize actions
    for (Action action : actions.values()) {
      action.setSite(site);
    }

    // Initialize renderers
    for (PageletRenderer renderer : renderers.values()) {
      renderer.setModule(this);
    }
  }

  /**
   * {@inheritDoc}
   *
   * @see ch.entwine.weblounge.common.site.Module#destroy()
   */
  public void destroy() {
    this.site = null;
  }

  /**
   * {@inheritDoc}
   *
   * @see ch.entwine.weblounge.common.site.Module#addAction(ch.entwine.weblounge.common.site.Action)
   */
  public void addAction(Action action) {
    actions.put(action.getIdentifier(), action);
    action.setModule(this);
    if (site != null)
      action.setSite(site);
  }

  /**
   * {@inheritDoc}
   *
   * @see ch.entwine.weblounge.common.site.Module#removeAction(ch.entwine.weblounge.common.site.Action)
   */
  public void removeAction(Action action) {
    actions.remove(action.getIdentifier());
    action.setModule(null);
    action.setSite(null);
  }

  /**
   * {@inheritDoc}
   *
   * @see ch.entwine.weblounge.common.site.Module#getAction(java.lang.String)
   */
  public Action getAction(String id) {
    return actions.get(id);
  }

  /**
   * {@inheritDoc}
   *
   * @see ch.entwine.weblounge.common.site.Module#getActions()
   */
  public Action[] getActions() {
    return actions.values().toArray(new Action[actions.size()]);
  }

  /**
   * {@inheritDoc}
   *
   * @see ch.entwine.weblounge.common.site.Module#addImageStyle(ch.entwine.weblounge.common.content.image.ImageStyle)
   */
  public void addImageStyle(ImageStyle imagestyle) {
    imagestyles.put(imagestyle.getIdentifier(), imagestyle);
  }

  /**
   * {@inheritDoc}
   *
   * @see ch.entwine.weblounge.common.site.Module#removeImageStyle(ch.entwine.weblounge.common.content.image.ImageStyle)
   */
  public void removeImageStyle(ImageStyle imagestyle) {
    imagestyles.remove(imagestyle.getIdentifier());
  }

  /**
   * {@inheritDoc}
   *
   * @see ch.entwine.weblounge.common.site.Module#getImageStyles()
   */
  public ImageStyle[] getImageStyles() {
    return imagestyles.values().toArray(new ImageStyle[imagestyles.size()]);
  }

  /**
   * {@inheritDoc}
   *
   * @see ch.entwine.weblounge.common.site.Module#getImageStyle(java.lang.String)
   */
  public ImageStyle getImageStyle(String id) {
    return imagestyles.get(id);
  }

  /**
   * {@inheritDoc}
   *
   * @see ch.entwine.weblounge.common.site.Module#addRenderer(ch.entwine.weblounge.common.content.page.PageletRenderer)
   */
  public void addRenderer(PageletRenderer renderer) {
    renderers.put(renderer.getIdentifier(), renderer);
    if (site != null) {
      renderer.setModule(this);
    }
  }

  /**
   * {@inheritDoc}
   *
   * @see ch.entwine.weblounge.common.site.Module#removeRenderer(ch.entwine.weblounge.common.content.page.PageletRenderer)
   */
  public void removeRenderer(PageletRenderer renderer) {
    renderers.remove(renderer.getIdentifier());
    renderer.setModule(null);
  }

  /**
   * {@inheritDoc}
   *
   * @see ch.entwine.weblounge.common.site.Module#getRenderer(java.lang.String)
   */
  public PageletRenderer getRenderer(String id) {
    return renderers.get(id);
  }

  /**
   * {@inheritDoc}
   *
   * @see ch.entwine.weblounge.common.site.Module#getRenderers()
   */
  public PageletRenderer[] getRenderers() {
    return renderers.values().toArray(new PageletRenderer[renderers.size()]);
  }

  /**
   * {@inheritDoc}
   *
   * @see ch.entwine.weblounge.common.site.Module#addJob(ch.entwine.weblounge.common.scheduler.Job)
   */
  public void addJob(Job job) {
    jobs.put(job.getIdentifier(), job);
  }

  /**
   * {@inheritDoc}
   *
   * @see ch.entwine.weblounge.common.site.Module#removeJob(ch.entwine.weblounge.common.scheduler.Job)
   */
  public void removeJob(Job job) {
    jobs.remove(job.getIdentifier());
  }

  /**
   * {@inheritDoc}
   *
   * @see ch.entwine.weblounge.common.site.Module#getJob(java.lang.String)
   */
  public Job getJob(String id) {
    return jobs.get(id);
  }

  /**
   * {@inheritDoc}
   *
   * @see ch.entwine.weblounge.common.site.Module#getJobs()
   */
  public Job[] getJobs() {
    return jobs.values().toArray(new Job[jobs.size()]);
  }

  /**
   * {@inheritDoc}
   *
   * @see ch.entwine.weblounge.common.site.Module#getSite()
   */
  public Site getSite() {
    return site;
  }

  /**
   * {@inheritDoc}
   *
   * @see ch.entwine.weblounge.common.site.Module#setName(java.lang.String)
   */
  public void setName(String title) {
    this.name = title;
  }

  /**
   * {@inheritDoc}
   *
   * @see ch.entwine.weblounge.common.site.Module#getName()
   */
  public String getName() {
    return name;
  }

  /**
   * {@inheritDoc}
   *
   * @see ch.entwine.weblounge.common.site.Module#setEnabled(boolean)
   */
  public void setEnabled(boolean enabled) {
    this.enabled = enabled;
  }

  /**
   * {@inheritDoc}
   *
   * @see ch.entwine.weblounge.common.site.Module#isEnabled()
   */
  public boolean isEnabled() {
    return enabled;
  }

  /**
   * {@inheritDoc}
   *
   * @see ch.entwine.weblounge.common.site.Module#setSearchable(boolean)
   */
  public void setSearchable(boolean searchable) {
    this.searchable = searchable;
  }

  /**
   * {@inheritDoc}
   *
   * @see ch.entwine.weblounge.common.site.Module#isSearchable()
   */
  public boolean isSearchable() {
    return searchable;
  }

  /**
   * {@inheritDoc}
   * <p>
   * This default implementation will always return an empty result set.
   * Subclasses returning <code>true</code> in {@link #isSearchable()} should
   * therefore overwrite this method.
   *
   * @see ch.entwine.weblounge.common.site.Module#search(java.lang.String)
   */
  public SearchResultItem[] search(String query) {
    return new SearchResultItem[] {};
  }

  /**
   * {@inheritDoc}
   *
   * @see ch.entwine.weblounge.common.site.Module#start()
   */
  public void start() throws ModuleException {
    logger.debug("Starting module {}", this);
    if (running)
      throw new IllegalStateException("Module is already running");
    if (!enabled)
      throw new IllegalStateException("Cannot start a disabled module");

    // Finally, mark this module as running
    running = true;
    logger.debug("Module '{}' started", this);

    // Tell listeners
    fireModuleStarted();
  }

  /**
   * {@inheritDoc}
   *
   * @see ch.entwine.weblounge.common.site.Module#stop()
   */
  public void stop() throws ModuleException {
    logger.debug("Stopping module {}", this);
    if (!running)
      throw new IllegalStateException("Module is not running");

    // Finally, mark this module as stopped
    running = false;
    logger.debug("Module '{}' stopped", this);

    // Tell listeners
    fireModuleStopped();
  }

  /**
   * {@inheritDoc}
   *
   * @see ch.entwine.weblounge.common.Customizable#setOption(java.lang.String,
   *      java.lang.String)
   */
  public void setOption(String name, String value) {
    options.setOption(name, value);
  }

  /**
   * {@inheritDoc}
   *
   * @see ch.entwine.weblounge.common.Customizable#setOption(java.lang.String,
   *      java.lang.String, ch.entwine.weblounge.common.site.Environment)
   */
  public void setOption(String name, String value, Environment environment) {
    options.setOption(name, value, environment);
  }

  /**
   * {@inheritDoc}
   *
   * @see ch.entwine.weblounge.common.Customizable#removeOption(java.lang.String)
   */
  public void removeOption(String name) {
    options.removeOption(name);
  }

  /**
   * {@inheritDoc}
   *
   * @see ch.entwine.weblounge.common.Customizable#getOptionValue(java.lang.String)
   */
  public String getOptionValue(String name) {
    return options.getOptionValue(name);
  }

  /**
   * {@inheritDoc}
   *
   * @see ch.entwine.weblounge.common.Customizable#getOptionValue(java.lang.String,
   *      java.lang.String)
   */
  public String getOptionValue(String name, String defaultValue) {
    return options.getOptionValue(name, defaultValue);
  }

  /**
   * {@inheritDoc}
   *
   * @see ch.entwine.weblounge.common.Customizable#getOptionValues(java.lang.String)
   */
  public String[] getOptionValues(String name) {
    return options.getOptionValues(name);
  }

  /**
   * {@inheritDoc}
   *
   * @see ch.entwine.weblounge.common.Customizable#hasOption(java.lang.String)
   */
  public boolean hasOption(String name) {
    return options.hasOption(name);
  }

  /**
   * {@inheritDoc}
   *
   * @see ch.entwine.weblounge.common.Customizable#getOptionNames()
   */
  public String[] getOptionNames() {
    return options.getOptionNames();
  }

  /**
   * {@inheritDoc}
   *
   * @see ch.entwine.weblounge.common.Customizable#getOptions()
   */
  public Map<String, Map<Environment, List<String>>> getOptions() {
    return options.getOptions();
  }

  /**
   * Method to fire a <code>moduleStarted()</code> message to all registered
   * <code>ModuleListener</code>s.
   */
  protected void fireModuleStarted() {
    if (moduleListeners == null)
      return;
    synchronized (moduleListeners) {
      for (ModuleListener listener : moduleListeners) {
        listener.moduleStarted(this);
      }
    }
  }

  /**
   * Method to fire a <code>moduleStopped()</code> message to all registered
   * <code>ModuleListener</code>s.
   */
  protected void fireModuleStopped() {
    if (moduleListeners == null)
      return;
    synchronized (moduleListeners) {
      for (ModuleListener listener : moduleListeners) {
        listener.moduleStopped(this);
      }
    }
  }

  /**
   * Initializes this module from an XML node that was generated using
   * {@link #toXml()}.
   * <p>
   * To speed things up, you might consider using the second signature that uses
   * an existing <code>XPath</code> instance instead of creating a new one.
   *
   * @param config
   *          the module node
   * @throws IllegalStateException
   *           if the module cannot be parsed
   * @see #fromXml(Node, XPath)
   * @see #toXml()
   */
  public static Module fromXml(Node config) throws IllegalStateException {
    XPath xpath = XPathFactory.newInstance().newXPath();

    // Define the xml namespace
    XPathNamespaceContext nsCtx = new XPathNamespaceContext(false);
    nsCtx.defineNamespaceURI("m", MODULE_XMLNS);
    xpath.setNamespaceContext(nsCtx);

    return fromXml(config, xpath);
  }

  /**
   * Initializes this module from an XML node that was generated using
   * {@link #toXml()}.
   *
   * @param config
   *          the module node
   * @param xpathProcessor
   *          xpath processor to use
   * @throws IllegalStateException
   *           if the module cannot be parsed
   * @see #toXml()
   */
  @SuppressWarnings("unchecked")
  public static Module fromXml(Node config, XPath xpathProcessor)
      throws IllegalStateException {

    ClassLoader classLoader = Thread.currentThread().getContextClassLoader();

    // identifier
    String identifier = XPathHelper.valueOf(config, "@id", xpathProcessor);
    if (identifier == null)
      throw new IllegalStateException("Unable to create module without identifier");

    // class
    Module module = null;
    String className = XPathHelper.valueOf(config, "m:class", xpathProcessor);
    if (className != null) {
      try {
        Class<? extends Module> c = (Class<? extends Module>) classLoader.loadClass(className);
        module = c.newInstance();
        module.setIdentifier(identifier);
      } catch (ClassNotFoundException e) {
        throw new IllegalStateException("Implementation " + className + " for module '" + identifier + "' not found", e);
      } catch (InstantiationException e) {
        throw new IllegalStateException("Error instantiating impelementation " + className + " for module '" + identifier + "'", e);
      } catch (IllegalAccessException e) {
        throw new IllegalStateException("Access violation instantiating implementation " + className + " for module '" + identifier + "'", e);
      } catch (Throwable t) {
        throw new IllegalStateException("Error loading implementation " + className + " for module '" + identifier + "'", t);
      }
    } else {
      module = new ModuleImpl();
      module.setIdentifier(identifier);
    }
   
    // Check if module is enabled
    Boolean enabled = Boolean.valueOf(XPathHelper.valueOf(config, "m:enable", xpathProcessor));
    module.setEnabled(enabled);
   
    // name
    String name = XPathHelper.valueOf(config, "m:name", xpathProcessor);
    module.setName(name);

    // pagelets
    NodeList pageletNodes = XPathHelper.selectList(config, "m:pagelets/m:pagelet", xpathProcessor);
    for (int i = 0; i < pageletNodes.getLength(); i++) {
      PageletRenderer pagelet = PageletRendererImpl.fromXml(pageletNodes.item(i), xpathProcessor);
      module.addRenderer(pagelet);
    }

    // actions
    NodeList actionNodes = XPathHelper.selectList(config, "m:actions/m:action", xpathProcessor);
    for (int i = 0; i < actionNodes.getLength(); i++) {
      module.addAction(ActionSupport.fromXml(actionNodes.item(i), xpathProcessor));
    }

    // image styles
    NodeList imagestyleNodes = XPathHelper.selectList(config, "m:imagestyles/m:imagestyle", xpathProcessor);
    for (int i = 0; i < imagestyleNodes.getLength(); i++) {
      module.addImageStyle(ImageStyleImpl.fromXml(imagestyleNodes.item(i), xpathProcessor));
    }

    // jobs
    NodeList jobNodes = XPathHelper.selectList(config, "m:jobs/m:job", xpathProcessor);
    for (int i = 0; i < jobNodes.getLength(); i++) {
      module.addJob(QuartzJob.fromXml(jobNodes.item(i), xpathProcessor));
    }

    // options
    Node optionsNode = XPathHelper.select(config, "m:options", xpathProcessor);
    OptionsHelper.fromXml(optionsNode, module, xpathProcessor);

    return module;
  }

  /**
   * {@inheritDoc}
   *
   * @see ch.entwine.weblounge.common.site.Module#toXml()
   */
  public String toXml() {
    StringBuffer b = new StringBuffer();
    b.append("<module id=\"");
    b.append(identifier);
    b.append("\" ");

    // namespace and schema
    b.append("xmlns=\"http://www.entwinemedia.com/weblounge/3.2/module\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocation=\"http://www.entwinemedia.com/weblounge/3.2/module http://www.entwinemedia.com/xsd/weblounge/3.2/module.xsd\"");

    b.append(">");

    // enable
    b.append("<enable>").append(enabled).append("</enable>");

    // Names
    if (StringUtils.isNotBlank(name)) {
      b.append("<name><![CDATA[");
      b.append(name);
      b.append("]]></name>");
    }

    // class
    if (!this.getClass().equals(ModuleImpl.class))
      b.append("<class>").append(getClass().getName()).append("</class>");

    // pagelets
    if (renderers.size() > 0) {
      b.append("<pagelets>");
      for (PageletRenderer renderer : renderers.values()) {
        b.append(renderer.toXml());
      }
      b.append("</pagelets>");
    }

    // actions
    if (actions.size() > 0) {
      b.append("<actions>");
      for (Action action : actions.values()) {
        b.append(action.toXml());
      }
      b.append("</actions>");
    }

    // jobs
    if (jobs.size() > 0) {
      b.append("<jobs>");
      for (Job job : jobs.values()) {
        b.append(job.toXml());
      }
      b.append("</jobs>");
    }

    // image styles
    if (imagestyles.size() > 0) {
      b.append("<imagestyles>");
      for (ImageStyle imagestyle : imagestyles.values()) {
        b.append(imagestyle.toXml());
      }
      b.append("</imagestyles>");
    }

    // Options
    b.append(options.toXml());

    b.append("</module>");
    return b.toString();
  }

  /**
   * Returns <code>true</code> if <code>obj</code> is a module featuring the
   * same module identifier than this one.
   *
   * @see java.lang.Object#equals(java.lang.Object)
   */
  @Override
  public boolean equals(Object obj) {
    if (obj != null && obj instanceof Module) {
      Module m = (Module) obj;
      if (identifier == null || m.getIdentifier() == null || !identifier.equals(m.getIdentifier()))
        return false;
      if (site == null && m.getSite() != null)
        return false;
      if (site != null && !site.equals(m.getSite()))
        return false;
      return true;
    }
    return false;
  }

  /**
   * Returns the module identifier's hash code.
   *
   * @see java.lang.Object#hashCode()
   */
  @Override
  public int hashCode() {
    if (identifier != null)
      return identifier.hashCode();
    else
      return super.hashCode();
  }

  /**
   * {@inheritDoc}
   *
   * @see java.lang.Object#toString()
   */
  @Override
  public String toString() {
    if (identifier != null)
      return identifier;
    else
      return super.toString();
  }

}
TOP

Related Classes of ch.entwine.weblounge.common.impl.site.ModuleImpl

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.