Package org.jspresso.framework.model.component.basic

Source Code of org.jspresso.framework.model.component.basic.AbstractComponentInvocationHandler

/*
* Copyright (c) 2005-2011 Vincent Vandenschrick. All rights reserved.
*
*  This file is part of the Jspresso framework.
*
*  Jspresso 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 3 of the License, or
*  (at your option) any later version.
*
*  Jspresso 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 Jspresso.  If not, see <http://www.gnu.org/licenses/>.
*/
package org.jspresso.framework.model.component.basic;

import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.Serializable;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.commons.beanutils.MethodUtils;
import org.jspresso.framework.model.component.ComponentException;
import org.jspresso.framework.model.component.IComponent;
import org.jspresso.framework.model.component.IComponentCollectionFactory;
import org.jspresso.framework.model.component.IComponentExtension;
import org.jspresso.framework.model.component.IComponentExtensionFactory;
import org.jspresso.framework.model.component.IComponentFactory;
import org.jspresso.framework.model.component.ILifecycleCapable;
import org.jspresso.framework.model.component.service.IComponentService;
import org.jspresso.framework.model.component.service.ILifecycleInterceptor;
import org.jspresso.framework.model.descriptor.IBooleanPropertyDescriptor;
import org.jspresso.framework.model.descriptor.ICollectionPropertyDescriptor;
import org.jspresso.framework.model.descriptor.IComponentDescriptor;
import org.jspresso.framework.model.descriptor.IModelDescriptorAware;
import org.jspresso.framework.model.descriptor.IPropertyDescriptor;
import org.jspresso.framework.model.descriptor.IReferencePropertyDescriptor;
import org.jspresso.framework.model.descriptor.IRelationshipEndPropertyDescriptor;
import org.jspresso.framework.model.entity.IEntity;
import org.jspresso.framework.model.entity.IEntityFactory;
import org.jspresso.framework.model.entity.IEntityLifecycleHandler;
import org.jspresso.framework.security.UserPrincipal;
import org.jspresso.framework.util.accessor.IAccessor;
import org.jspresso.framework.util.accessor.IAccessorFactory;
import org.jspresso.framework.util.accessor.ICollectionAccessor;
import org.jspresso.framework.util.bean.AccessorInfo;
import org.jspresso.framework.util.bean.EAccessorType;
import org.jspresso.framework.util.bean.IPropertyChangeCapable;
import org.jspresso.framework.util.bean.SinglePropertyChangeSupport;
import org.jspresso.framework.util.collection.CollectionHelper;
import org.jspresso.framework.util.lang.ObjectUtils;

/**
* This is the core implementation of all components in the application.
* Instances of this class serve as handlers for proxies representing the
* components.
*
* @version $LastChangedRevision: 4617 $
* @author Vincent Vandenschrick
*/
public abstract class AbstractComponentInvocationHandler implements
    InvocationHandler, Serializable {

  private static final long                                                            serialVersionUID = -8332414648339056836L;

  private IAccessorFactory                                                             accessorFactory;
  private SinglePropertyChangeSupport                                                  changeSupport;
  private IComponentCollectionFactory<IComponent>                                      collectionFactory;
  private IComponentDescriptor<? extends IComponent>                                   componentDescriptor;
  private Map<Class<IComponentExtension<IComponent>>, IComponentExtension<IComponent>> componentExtensions;
  private IComponentExtensionFactory                                                   extensionFactory;
  private IComponentFactory                                                            inlineComponentFactory;
  private Set<String>                                                                  modifierMonitors;
  private boolean                                                                      propertyProcessorsEnabled;

  private Map<String, InlineReferenceTracker>                                          referenceTrackers;

  /**
   * Constructs a new <code>BasicComponentInvocationHandler</code> instance.
   *
   * @param componentDescriptor
   *          The descriptor of the proxy component.
   * @param inlineComponentFactory
   *          the factory used to create inline components.
   * @param collectionFactory
   *          The factory used to create empty component collections from
   *          collection getters.
   * @param accessorFactory
   *          The factory used to access proxy properties.
   * @param extensionFactory
   *          The factory used to create component extensions based on their
   *          classes.
   */
  protected AbstractComponentInvocationHandler(
      IComponentDescriptor<? extends IComponent> componentDescriptor,
      IComponentFactory inlineComponentFactory,
      IComponentCollectionFactory<IComponent> collectionFactory,
      IAccessorFactory accessorFactory,
      IComponentExtensionFactory extensionFactory) {
    this.componentDescriptor = componentDescriptor;
    this.inlineComponentFactory = inlineComponentFactory;
    this.collectionFactory = collectionFactory;
    this.accessorFactory = accessorFactory;
    this.extensionFactory = extensionFactory;
    this.propertyProcessorsEnabled = true;
    this.referenceTrackers = new HashMap<String, InlineReferenceTracker>();
  }

  /**
   * Gets the interface class being the contract of this component.
   *
   * @return the component interface contract.
   */
  public Class<? extends Object> getComponentContract() {
    return componentDescriptor.getComponentContract();
  }

  /**
   * Handles methods invocations on the component proxy. Either : <li>delegates
   * to one of its extension if the accessed property is registered as being
   * part of an extension <li>handles property access internally
   * <p>
   * {@inheritDoc}
   */
  @Override
  @SuppressWarnings("unchecked")
  public synchronized Object invoke(Object proxy, Method method, Object[] args)
      throws Throwable {
    String methodName = method.getName();
    if ("hashCode".equals(methodName)) {
      return new Integer(computeHashCode((IComponent) proxy));
    } else if ("equals".equals(methodName)) {
      return new Boolean(computeEquals((IComponent) proxy, args[0]));
    } else if ("toString".equals(methodName)) {
      return toString(proxy);
    } else if ("getComponentContract".equals(methodName)) {
      return componentDescriptor.getComponentContract();
    } else if ("addPropertyChangeListener".equals(methodName)) {
      if (args.length == 1) {
        addPropertyChangeListener(proxy, (PropertyChangeListener) args[0]);
        return null;
      }
      addPropertyChangeListener(proxy, (String) args[0],
          (PropertyChangeListener) args[1]);
      return null;
    } else if ("removePropertyChangeListener".equals(methodName)) {
      if (args.length == 1) {
        removePropertyChangeListener((PropertyChangeListener) args[0]);
        return null;
      }
      removePropertyChangeListener((String) args[0],
          (PropertyChangeListener) args[1]);
      return null;
    } else if ("firePropertyChange".equals(methodName)) {
      firePropertyChange(proxy, (String) args[0], args[1], args[2]);
      return null;
    } else if ("straightSetProperty".equals(methodName)) {
      straightSetProperty(proxy, (String) args[0], args[1]);
      return null;
    } else if ("straightGetProperty".equals(methodName)) {
      return straightGetProperty(proxy, (String) args[0]);
    } else if ("straightSetProperties".equals(methodName)) {
      straightSetProperties(proxy, (Map<String, Object>) args[0]);
      return null;
    } else if ("straightGetProperties".equals(methodName)) {
      return straightGetProperties(proxy);
    } else if ("setPropertyProcessorsEnabled".equals(methodName)) {
      propertyProcessorsEnabled = ((Boolean) args[0]).booleanValue();
      return null;
    } else {
      boolean isLifecycleMethod = false;
      try {
        isLifecycleMethod = ILifecycleCapable.class.getMethod(methodName,
            method.getParameterTypes()) != null;
      } catch (NoSuchMethodException ignored) {
        // this is certainly normal.
      }
      if (isLifecycleMethod) {
        return new Boolean(invokeLifecycleInterceptors(proxy, method, args));
      }
      AccessorInfo accessorInfo = new AccessorInfo(method);
      EAccessorType accessorType = accessorInfo.getAccessorType();
      IPropertyDescriptor propertyDescriptor = null;
      if (accessorType != EAccessorType.NONE) {
        String accessedPropertyName = accessorInfo.getAccessedPropertyName();
        if (accessedPropertyName != null) {
          propertyDescriptor = componentDescriptor
              .getPropertyDescriptor(accessedPropertyName);
        }
      }
      if (propertyDescriptor != null) {
        Class<IComponentExtension<IComponent>> extensionClass = (Class<IComponentExtension<IComponent>>) propertyDescriptor
            .getDelegateClass();
        if (extensionClass != null) {
          IComponentExtension<? extends IComponent> extensionDelegate = getExtensionInstance(
              extensionClass, (IComponent) proxy);
          return invokeExtensionMethod(extensionDelegate, method, args);
        } else if (!propertyDescriptor.isComputed()) {
          if (accessorInfo.isModifier()) {
            if (modifierMonitors != null
                && modifierMonitors.contains(methodName)) {
              return null;
            }
            if (modifierMonitors == null) {
              modifierMonitors = new HashSet<String>();
            }
            modifierMonitors.add(methodName);
          }
          try {
            switch (accessorType) {
              case GETTER:
                return getProperty(proxy, propertyDescriptor);
              case SETTER:
                setProperty(proxy, propertyDescriptor, args[0]);
                return null;
              case ADDER:
                if (args.length == 2) {
                  addToProperty(proxy,
                      (ICollectionPropertyDescriptor<?>) propertyDescriptor,
                      ((Integer) args[0]).intValue(), args[1]);
                } else {
                  addToProperty(proxy,
                      (ICollectionPropertyDescriptor<?>) propertyDescriptor,
                      args[0]);
                }
                return null;
              case REMOVER:
                removeFromProperty(proxy,
                    (ICollectionPropertyDescriptor<?>) propertyDescriptor,
                    args[0]);
                return null;
              default:
                break;
            }
          } finally {
            if (modifierMonitors != null && accessorInfo.isModifier()) {
              modifierMonitors.remove(methodName);
            }
          }
        } else {
          try {
            return invokeServiceMethod(proxy, method, args);
          } catch (NoSuchMethodException ignored) {
            // it will fall back in the general case.
          }
          throw new ComponentException(
              "The '"
                  + propertyDescriptor.getName()
                  + "' property is described as computed but we couldn't determine a way to compute it,"
                  + " either through an extension or a service delegate on the following component : \n"
                  + componentDescriptor.getComponentContract().getName());
        }
      } else {
        try {
          return invokeServiceMethod(proxy, method, args);
        } catch (NoSuchMethodException ignored) {
          // it will fall back in the general case.
        }
      }
    }
    throw new ComponentException(method.toString()
        + " is not supported on the component "
        + componentDescriptor.getComponentContract().getName());
  }

  /**
   * Sets the collectionFactory.
   *
   * @param collectionFactory
   *          the collectionFactory to set.
   */
  public void setCollectionFactory(
      IComponentCollectionFactory<IComponent> collectionFactory) {
    this.collectionFactory = collectionFactory;
  }

  /**
   * Delegate method to compute object equality.
   *
   * @param proxy
   *          the target component to compute equality of.
   * @param another
   *          the object to compute equality against.
   * @return the computed equality.
   */
  protected abstract boolean computeEquals(IComponent proxy, Object another);

  /**
   * Delegate method to compute hashcode.
   *
   * @param proxy
   *          the target component to compute hashcode for.
   * @return the computed hashcode.
   */
  protected abstract int computeHashCode(IComponent proxy);

  /**
   * Gives a chance to configure created extensions.
   *
   * @param extension
   *          the extension to configure.
   */
  protected void configureExtension(IComponentExtension<IComponent> extension) {
    // Empty by default.
  }

  /**
   * Gives a chance to the implementor to decorate a component reference before
   * returning it when fetching association ends.
   *
   * @param referent
   *          the component reference to decorate.
   * @param referentDescriptor
   *          the component descriptor of the referent.
   * @return the decorated component.
   */
  protected abstract IComponent decorateReferent(IComponent referent,
      IComponentDescriptor<? extends IComponent> referentDescriptor);

  /**
   * An empty hook that gets called whenever an entity is detached from a parent
   * one.
   *
   * @param child
   *          the child entity.
   */
  protected void entityDetached(IEntity child) {
    // defaults to no-op.
  }

  /**
   * Gets the accessorFactory.
   *
   * @return the accessorFactory.
   */
  protected IAccessorFactory getAccessorFactory() {
    return accessorFactory;
  }

  /**
   * Gets a collection property value.
   *
   * @param proxy
   *          the proxy to get the property of.
   * @param propertyDescriptor
   *          the property descriptor to get the value for.
   * @return the property value.
   */
  @SuppressWarnings("unchecked")
  protected Object getCollectionProperty(Object proxy,
      ICollectionPropertyDescriptor<? extends IComponent> propertyDescriptor) {
    Object property = straightGetProperty(proxy, propertyDescriptor.getName());
    if (property == null) {
      property = collectionFactory.createComponentCollection(propertyDescriptor
          .getReferencedDescriptor().getCollectionInterface());
      storeProperty(propertyDescriptor.getName(), property);
    }
    if (property instanceof List) {
      List<IComponent> propertyAsList = (List<IComponent>) property;
      for (int i = 0; i < propertyAsList.size(); i++) {
        IComponent referent = propertyAsList.get(i);
        IComponent decorated = decorateReferent(referent, propertyDescriptor
            .getReferencedDescriptor().getElementDescriptor()
            .getComponentDescriptor());
        if (decorated != referent) {
          propertyAsList.set(i, decorated);
        }
      }
    } else if (property instanceof Set) {
      Set<IComponent> propertyAsSet = (Set<IComponent>) property;
      for (IComponent referent : new HashSet<IComponent>(propertyAsSet)) {
        IComponent decorated = decorateReferent(referent, propertyDescriptor
            .getReferencedDescriptor().getElementDescriptor()
            .getComponentDescriptor());
        if (decorated != referent) {
          propertyAsSet.add(decorated);
        }
      }
    }
    inlineComponentFactory.sortCollectionProperty((IComponent) proxy,
        propertyDescriptor.getName());
    return property;
  }

  /**
   * Creates and registers an extension instance.
   *
   * @param extensionClass
   *          the extension class.
   * @param proxy
   *          the proxy to register the extension on.
   * @return the component extension.
   */
  protected synchronized IComponentExtension<? extends IComponent> getExtensionInstance(
      Class<IComponentExtension<IComponent>> extensionClass, IComponent proxy) {
    IComponentExtension<IComponent> extension;
    if (componentExtensions == null) {
      componentExtensions = new HashMap<Class<IComponentExtension<IComponent>>, IComponentExtension<IComponent>>();
      extension = null;
    } else {
      extension = componentExtensions.get(extensionClass);
    }
    if (extension == null) {
      extension = extensionFactory.createComponentExtension(extensionClass,
          componentDescriptor.getComponentContract(), proxy);
      configureExtension(extension);
      componentExtensions.put(extensionClass, extension);
    }
    return extension;
  }

  /**
   * Gets the inlineComponentFactory.
   *
   * @return the inlineComponentFactory.
   */
  protected IComponentFactory getInlineComponentFactory() {
    return inlineComponentFactory;
  }

  /**
   * Gets a property value.
   *
   * @param proxy
   *          the proxy to get the property of.
   * @param propertyDescriptor
   *          the property descriptor to get the value for.
   * @return the property value.
   */
  @SuppressWarnings("unchecked")
  protected Object getProperty(Object proxy,
      IPropertyDescriptor propertyDescriptor) {
    if (propertyDescriptor instanceof ICollectionPropertyDescriptor) {
      return getCollectionProperty(
          proxy,
          (ICollectionPropertyDescriptor<? extends IComponent>) propertyDescriptor);
    } else if (propertyDescriptor instanceof IReferencePropertyDescriptor) {
      return getReferenceProperty(proxy,
          (IReferencePropertyDescriptor<IComponent>) propertyDescriptor);
    }
    Object propertyValue = straightGetProperty(proxy,
        propertyDescriptor.getName());
    return propertyValue;
  }

  /**
   * Gets a reference property value.
   *
   * @param proxy
   *          the proxy to get the property of.
   * @param propertyDescriptor
   *          the property descriptor to get the value for.
   * @return the property value.
   */
  protected Object getReferenceProperty(Object proxy,
      final IReferencePropertyDescriptor<IComponent> propertyDescriptor) {
    IComponent property = (IComponent) straightGetProperty(proxy,
        propertyDescriptor.getName());
    if (property == null && isInlineComponentReference(propertyDescriptor)) {
      property = inlineComponentFactory
          .createComponentInstance(propertyDescriptor.getReferencedDescriptor()
              .getComponentContract());
      storeReferenceProperty(propertyDescriptor, null, property);
    }
    return decorateReferent(property,
        propertyDescriptor.getReferencedDescriptor());
  }

  /**
   * Invokes a service method on the component.
   *
   * @param proxy
   *          the component to invoke the service on.
   * @param method
   *          the method implemented by the component.
   * @param args
   *          the arguments of the method implemented by the component.
   * @return the value returned by the method execution if any.
   * @throws NoSuchMethodException
   *           if no mean could be found to service the method.
   */
  protected Object invokeServiceMethod(Object proxy, Method method,
      Object[] args) throws NoSuchMethodException {
    IComponentService service = componentDescriptor.getServiceDelegate(method);
    if (service != null) {
      int signatureSize = method.getParameterTypes().length + 1;
      Class<?>[] parameterTypes = new Class[signatureSize];
      Object[] parameters = new Object[signatureSize];

      parameterTypes[0] = componentDescriptor.getComponentContract();
      parameters[0] = proxy;

      for (int i = 1; i < signatureSize; i++) {
        parameterTypes[i] = method.getParameterTypes()[i - 1];
        parameters[i] = args[i - 1];
      }
      try {
        return MethodUtils.invokeMethod(service, method.getName(), parameters,
            parameterTypes);
      } catch (IllegalAccessException ex) {
        throw new ComponentException(ex);
      } catch (InvocationTargetException ex) {
        if (ex.getCause() instanceof RuntimeException) {
          throw (RuntimeException) ex.getCause();
        }
        throw new ComponentException(ex.getCause());
      } catch (NoSuchMethodException ex) {
        throw new ComponentException(ex);
      }
    }
    throw new NoSuchMethodException(method.toString());
  }

  /**
   * Wether the object is fully initialized.
   *
   * @param objectOrProxy
   *          the object to test.
   * @return true if the object is fully initialized.
   */
  protected boolean isInitialized(Object objectOrProxy) {
    return true;
  }

  /**
   * Gets wether this reference descriptor points to an inline component.
   *
   * @param propertyDescriptor
   *          the reference descriptor to test.
   * @return true if this reference descriptor points to an inline component.
   */
  protected boolean isInlineComponentReference(
      IReferencePropertyDescriptor<?> propertyDescriptor) {
    return !IEntity.class.isAssignableFrom(propertyDescriptor
        .getReferencedDescriptor().getComponentContract())
        && !propertyDescriptor.getReferencedDescriptor().isPurelyAbstract()
        && !propertyDescriptor.isComputed();
  }

  /**
   * An empty hook that gets called whenever an entity is to be updated.
   *
   * @param entityFactory
   *          an entity factory instance which can be used to complete the
   *          lifecycle step.
   * @param principal
   *          the principal triggering the action.
   * @param entityLifecycleHandler
   *          entityLifecycleHandler.
   */
  protected void onUpdate(IEntityFactory entityFactory,
      UserPrincipal principal, IEntityLifecycleHandler entityLifecycleHandler) {
    // defaults to no-op.
  }

  /**
   * Direct read access to the properties map without any other operation. Use
   * with caution only in subclasses.
   *
   * @param propertyName
   *          the property name.
   * @return the property value.
   */
  protected abstract Object retrievePropertyValue(String propertyName);

  /**
   * Direct write access to the properties map without any other operation. Use
   * with caution only in subclasses.
   *
   * @param propertyName
   *          the property name.
   * @param propertyValue
   *          the property value.
   */
  protected abstract void storeProperty(String propertyName,
      Object propertyValue);

  /**
   * Performs necessary registration on inline components before actually
   * storing them.
   *
   * @param propertyDescriptor
   *          the reference property descriptor.
   * @param oldPropertyValue
   *          the old reference property value.
   * @param newPropertyValue
   *          the new reference property value.
   */
  protected void storeReferenceProperty(
      IReferencePropertyDescriptor<?> propertyDescriptor,
      Object oldPropertyValue, Object newPropertyValue) {
    String propertyName = propertyDescriptor.getName();
    InlineReferenceTracker oldTracker = null;
    if (oldPropertyValue != null) {
      oldTracker = referenceTrackers.get(propertyName);
      if (oldTracker != null) {
        ((IPropertyChangeCapable) oldPropertyValue)
            .removePropertyChangeListener(oldTracker);
      }
      referenceTrackers.remove(propertyName);
    }
    InlineReferenceTracker newTracker = null;
    if (newPropertyValue != null) {
      newTracker = new InlineReferenceTracker(propertyName,
          isInlineComponentReference(propertyDescriptor));
      ((IPropertyChangeCapable) newPropertyValue)
          .addPropertyChangeListener(newTracker);
      referenceTrackers.put(propertyName, newTracker);
    }
    storeProperty(propertyName, newPropertyValue);
    if (newTracker != null) {
      if (newPropertyValue instanceof IComponent) {
        for (Map.Entry<String, Object> property : ((IComponent) newPropertyValue)
            .straightGetProperties().entrySet()) {
          if (oldPropertyValue instanceof IComponent) {
            newTracker.propertyChange(new PropertyChangeEvent(newPropertyValue,
                property.getKey(), null, property.getValue()));
          } else {
            newTracker.propertyChange(new PropertyChangeEvent(newPropertyValue,
                property.getKey(), null, property.getValue()));
          }
        }
      }
    } else if (oldTracker != null) {
      if (oldPropertyValue instanceof IComponent) {
        for (Map.Entry<String, Object> property : ((IComponent) oldPropertyValue)
            .straightGetProperties().entrySet()) {
          oldTracker.propertyChange(new PropertyChangeEvent(oldPropertyValue,
              property.getKey(), property.getValue(), null));
        }
      }
    }
  }

  /**
   * Directly gets all property values out of the property store without any
   * other operation.
   *
   * @param proxy
   *          the proxy to straight get the properties from.
   * @return The map of properties.
   */
  protected Map<String, Object> straightGetProperties(Object proxy) {
    Map<String, Object> allProperties = new HashMap<String, Object>();
    for (IPropertyDescriptor propertyDescriptor : componentDescriptor
        .getPropertyDescriptors()) {
      String propertyName = propertyDescriptor.getName();
      if (!propertyDescriptor.isComputed()) {
        allProperties.put(propertyName,
            straightGetProperty(proxy, propertyName));
      }
    }
    return allProperties;
  }

  /**
   * Directly get a property value out of the property store without any other
   * operation.
   *
   * @param proxy
   *          the proxy to straight get the property from.
   * @param propertyName
   *          the name of the property.
   * @return the property value or null.
   */
  protected Object straightGetProperty(Object proxy, String propertyName) {
    IPropertyDescriptor propertyDescriptor = componentDescriptor
        .getPropertyDescriptor(propertyName);
    if (propertyDescriptor == null || propertyDescriptor.isComputed()) {
      return null;
    }
    Object propertyValue = retrievePropertyValue(propertyName);
    if (propertyValue == null
        && propertyDescriptor instanceof IBooleanPropertyDescriptor) {
      return Boolean.FALSE;
    }
    return propertyValue;
  }

  /**
   * Directly set a property value to the property store without any other
   * operation.
   *
   * @param proxy
   *          the proxy to straight set the property to.
   * @param propertyName
   *          the name of the property.
   * @param newPropertyValue
   *          the property value or null.
   */
  protected void straightSetProperty(Object proxy, String propertyName,
      Object newPropertyValue) {
    IPropertyDescriptor propertyDescriptor = componentDescriptor
        .getPropertyDescriptor(propertyName);
    if (propertyDescriptor == null || propertyDescriptor.isComputed()) {
      return;
    }
    Object currentPropertyValue = straightGetProperty(proxy, propertyName);
    if (propertyDescriptor instanceof IReferencePropertyDescriptor) {
      // reference must change sometimes even if entities are equal.
      if (/* !ObjectUtils.equals(currentPropertyValue, newPropertyValue) */currentPropertyValue != newPropertyValue) {
        storeReferenceProperty(
            (IReferencePropertyDescriptor<?>) propertyDescriptor,
            currentPropertyValue, newPropertyValue);
      }
    } else {
      storeProperty(propertyName, newPropertyValue);
    }
    if (propertyDescriptor instanceof ICollectionPropertyDescriptor) {
      if (currentPropertyValue != null
          && currentPropertyValue == newPropertyValue
          && isInitialized(currentPropertyValue)) {
        currentPropertyValue = Proxy.newProxyInstance(
            Thread.currentThread().getContextClassLoader(),
            new Class[] {
              ((ICollectionPropertyDescriptor<?>) propertyDescriptor)
                  .getReferencedDescriptor().getCollectionInterface()
            },
            new NeverEqualsInvocationHandler(CollectionHelper
                .cloneCollection((Collection<?>) currentPropertyValue)));
      }
    }
    firePropertyChange(propertyName, currentPropertyValue, newPropertyValue);
  }

  private synchronized void addPropertyChangeListener(Object proxy,
      PropertyChangeListener listener) {
    if (listener == null) {
      return;
    }
    if (changeSupport == null) {
      changeSupport = new SinglePropertyChangeSupport(proxy);
    }
    changeSupport.addPropertyChangeListener(listener);
  }

  private synchronized void addPropertyChangeListener(Object proxy,
      String propertyName, PropertyChangeListener listener) {
    if (listener == null) {
      return;
    }
    if (changeSupport == null) {
      changeSupport = new SinglePropertyChangeSupport(proxy);
    }
    changeSupport.addPropertyChangeListener(propertyName, listener);
  }

  @SuppressWarnings("unchecked")
  private void addToProperty(Object proxy,
      ICollectionPropertyDescriptor<?> propertyDescriptor, int index,
      Object value) {
    String propertyName = propertyDescriptor.getName();
    Collection<?> collectionProperty = null;
    try {
      collectionProperty = (Collection<?>) accessorFactory
          .createPropertyAccessor(propertyName,
              componentDescriptor.getComponentContract()).getValue(proxy);
    } catch (IllegalAccessException ex) {
      throw new ComponentException(ex);
    } catch (InvocationTargetException ex) {
      if (ex.getCause() instanceof RuntimeException) {
        throw (RuntimeException) ex.getCause();
      }
      throw new ComponentException(ex.getCause());
    } catch (NoSuchMethodException ex) {
      throw new ComponentException(ex);
    }
    try {
      if (propertyProcessorsEnabled) {
        propertyDescriptor.preprocessAdder(proxy, collectionProperty, value);
      }
      IRelationshipEndPropertyDescriptor reversePropertyDescriptor = propertyDescriptor
          .getReverseRelationEnd();
      if (reversePropertyDescriptor != null) {
        if (reversePropertyDescriptor instanceof IReferencePropertyDescriptor<?>) {
          accessorFactory.createPropertyAccessor(
              reversePropertyDescriptor.getName(),
              propertyDescriptor.getReferencedDescriptor()
                  .getElementDescriptor().getComponentContract()).setValue(
              value, proxy);
        } else if (reversePropertyDescriptor instanceof ICollectionPropertyDescriptor<?>) {
          ICollectionAccessor collectionAccessor = accessorFactory
              .createCollectionPropertyAccessor(
                  reversePropertyDescriptor.getName(),
                  propertyDescriptor.getReferencedDescriptor()
                      .getElementDescriptor().getComponentContract(),
                  ((ICollectionPropertyDescriptor<?>) reversePropertyDescriptor)
                      .getCollectionDescriptor().getElementDescriptor()
                      .getComponentContract());
          if (collectionAccessor instanceof IModelDescriptorAware) {
            ((IModelDescriptorAware) collectionAccessor)
                .setModelDescriptor(reversePropertyDescriptor);
          }
          collectionAccessor.addToValue(value, proxy);
        }
      }
      Collection<?> oldCollectionSnapshot = CollectionHelper
          .cloneCollection((Collection<?>) collectionProperty);
      boolean inserted = false;
      if (collectionProperty instanceof List<?> && index >= 0
          && index < collectionProperty.size()) {
        ((List<Object>) collectionProperty).add(index, value);
        inserted = true;
      } else {
        inserted = ((Collection<Object>) collectionProperty).add(value);
      }
      if (inserted) {
        inlineComponentFactory.sortCollectionProperty((IComponent) proxy,
            propertyName);
        firePropertyChange(propertyName, oldCollectionSnapshot,
            collectionProperty);
        if (propertyProcessorsEnabled) {
          propertyDescriptor.postprocessAdder(proxy, collectionProperty, value);
        }
      }
    } catch (RuntimeException ex) {
      rollbackProperty(proxy, propertyDescriptor, collectionProperty);
      throw ex;
    } catch (InvocationTargetException ex) {
      rollbackProperty(proxy, propertyDescriptor, collectionProperty);
      if (ex.getCause() instanceof RuntimeException) {
        throw (RuntimeException) ex.getCause();
      }
      throw new ComponentException(ex.getCause());
    } catch (IllegalAccessException ex) {
      throw new ComponentException(ex);
    } catch (NoSuchMethodException ex) {
      throw new ComponentException(ex);
    }
  }

  private void addToProperty(Object proxy,
      ICollectionPropertyDescriptor<?> propertyDescriptor, Object value) {
    addToProperty(proxy, propertyDescriptor, -1, value);
  }

  private void checkIntegrity(Object proxy) {
    if (propertyProcessorsEnabled) {
      for (IPropertyDescriptor propertyDescriptor : componentDescriptor
          .getPropertyDescriptors()) {
        if (!propertyDescriptor.isComputed()) {
          propertyDescriptor.preprocessSetter(proxy,
              straightGetProperty(proxy, propertyDescriptor.getName()));
        }
      }
    }
  }

  private void firePropertyChange(Object proxy, String propertyName,
      Object oldValue, Object newValue) {
    firePropertyChange(propertyName, oldValue, newValue);
    // This method supports firing nested property changes
    if (propertyName != null) {
      int lastIndexOfDelim = propertyName.lastIndexOf(IAccessor.NESTED_DELIM);
      if (lastIndexOfDelim > 0) {
        Object propertyHolder;
        try {
          propertyHolder = getAccessorFactory().createPropertyAccessor(
              propertyName.substring(0, lastIndexOfDelim),
              getComponentContract()).getValue(proxy);
          if (propertyHolder != null && propertyHolder instanceof IComponent) {
            ((IComponent) propertyHolder).firePropertyChange(
                propertyName.substring(lastIndexOfDelim + 1), oldValue,
                newValue);
          }
        } catch (IllegalAccessException ex) {
          throw new ComponentException(ex);
        } catch (InvocationTargetException ex) {
          if (ex.getCause() instanceof RuntimeException) {
            throw (RuntimeException) ex.getCause();
          }
          throw new ComponentException(ex.getCause());
        } catch (NoSuchMethodException ex) {
          throw new ComponentException(ex);
        }
      }
    }
  }

  private void firePropertyChange(String propertyName, Object oldValue,
      Object newValue) {
    if (changeSupport == null || (oldValue == null && newValue == null)
        || (oldValue == newValue)) {
      return;
    }
    if (!isInitialized(oldValue) || !isInitialized(newValue)) {
      changeSupport.firePropertyChange(propertyName, null, newValue);
    } else {
      changeSupport.firePropertyChange(propertyName, oldValue, newValue);
    }
  }

  private Object invokeExtensionMethod(
      IComponentExtension<? extends IComponent> componentExtension,
      Method method, Object[] args) {
    try {
      return MethodUtils.invokeMethod(componentExtension, method.getName(),
          args, method.getParameterTypes());
    } catch (IllegalAccessException ex) {
      throw new ComponentException(ex);
    } catch (InvocationTargetException ex) {
      if (ex.getCause() instanceof RuntimeException) {
        throw (RuntimeException) ex.getCause();
      }
      throw new ComponentException(ex.getCause());
    } catch (NoSuchMethodException ex) {
      throw new ComponentException(ex);
    }
  }

  @SuppressWarnings("unchecked")
  private boolean invokeLifecycleInterceptors(Object proxy,
      Method lifecycleMethod, Object[] args) {
    if (ILifecycleCapable.ON_UPDATE_METHOD_NAME.equals(lifecycleMethod
        .getName())) {
      onUpdate((IEntityFactory) args[0], (UserPrincipal) args[1],
          (IEntityLifecycleHandler) args[2]);
    }
    boolean interceptorResults = false;
    for (ILifecycleInterceptor<?> lifecycleInterceptor : componentDescriptor
        .getLifecycleInterceptors()) {
      int signatureSize = lifecycleMethod.getParameterTypes().length + 1;
      Class<?>[] parameterTypes = new Class[signatureSize];
      Object[] parameters = new Object[signatureSize];

      parameterTypes[0] = componentDescriptor.getComponentContract();
      parameters[0] = proxy;

      for (int i = 1; i < signatureSize; i++) {
        parameterTypes[i] = lifecycleMethod.getParameterTypes()[i - 1];
        parameters[i] = args[i - 1];
      }
      try {
        Object interceptorResult = MethodUtils.invokeMethod(
            lifecycleInterceptor, lifecycleMethod.getName(), parameters,
            parameterTypes);
        if (interceptorResult instanceof Boolean) {
          interceptorResults = interceptorResults
              || ((Boolean) interceptorResult).booleanValue();
        }
      } catch (IllegalAccessException ex) {
        throw new ComponentException(ex);
      } catch (InvocationTargetException ex) {
        if (ex.getCause() instanceof RuntimeException) {
          throw (RuntimeException) ex.getCause();
        }
        throw new ComponentException(ex.getCause());
      } catch (NoSuchMethodException ex) {
        throw new ComponentException(ex);
      }
    }
    // invoke lifecycle method on inlined components
    for (IPropertyDescriptor propertyDescriptor : componentDescriptor
        .getPropertyDescriptors()) {
      if (propertyDescriptor instanceof IReferencePropertyDescriptor<?>
          && isInlineComponentReference((IReferencePropertyDescriptor<IComponent>) propertyDescriptor)) {
        Object inlineComponent = getProperty(proxy, propertyDescriptor);
        if (inlineComponent != null) {
          try {
            Object interceptorResult = MethodUtils.invokeMethod(
                inlineComponent, lifecycleMethod.getName(), args,
                lifecycleMethod.getParameterTypes());
            if (interceptorResult instanceof Boolean) {
              interceptorResults = interceptorResults
                  || ((Boolean) interceptorResult).booleanValue();
            }
          } catch (NoSuchMethodException ex) {
            throw new ComponentException(ex);
          } catch (IllegalAccessException ex) {
            throw new ComponentException(ex);
          } catch (InvocationTargetException ex) {
            if (ex.getCause() instanceof RuntimeException) {
              throw (RuntimeException) ex.getCause();
            }
            throw new ComponentException(ex.getCause());
          }
        }
      }
    }
    if (ILifecycleCapable.ON_PERSIST_METHOD_NAME.equals(lifecycleMethod
        .getName())) {
      // Important to check for not null values.
      checkIntegrity(proxy);
    }
    return interceptorResults;
  }

  private void removeFromProperty(Object proxy,
      ICollectionPropertyDescriptor<?> propertyDescriptor, Object value) {
    String propertyName = propertyDescriptor.getName();
    // The following optim breaks bidi N-N relationship persistence
    // if (!isInitialized(straightGetProperty(proxy, propertyName))) {
    // return;
    // }
    Collection<?> collectionProperty = null;
    try {
      collectionProperty = (Collection<?>) accessorFactory
          .createPropertyAccessor(propertyName,
              componentDescriptor.getComponentContract()).getValue(proxy);
    } catch (IllegalAccessException ex) {
      throw new ComponentException(ex);
    } catch (InvocationTargetException ex) {
      if (ex.getCause() instanceof RuntimeException) {
        throw (RuntimeException) ex.getCause();
      }
      throw new ComponentException(ex.getCause());
    } catch (NoSuchMethodException ex) {
      throw new ComponentException(ex);
    }
    try {
      if (propertyProcessorsEnabled) {
        propertyDescriptor.preprocessRemover(proxy, collectionProperty, value);
      }
      if (collectionProperty.contains(value)) {
        IRelationshipEndPropertyDescriptor reversePropertyDescriptor = propertyDescriptor
            .getReverseRelationEnd();
        if (reversePropertyDescriptor != null) {
          if (reversePropertyDescriptor instanceof IReferencePropertyDescriptor<?>) {
            accessorFactory.createPropertyAccessor(
                reversePropertyDescriptor.getName(),
                propertyDescriptor.getReferencedDescriptor()
                    .getElementDescriptor().getComponentContract()).setValue(
                value, null);
          } else if (reversePropertyDescriptor instanceof ICollectionPropertyDescriptor<?>) {
            ICollectionAccessor collectionAccessor = accessorFactory
                .createCollectionPropertyAccessor(
                    reversePropertyDescriptor.getName(),
                    propertyDescriptor.getReferencedDescriptor()
                        .getElementDescriptor().getComponentContract(),
                    ((ICollectionPropertyDescriptor<?>) reversePropertyDescriptor)
                        .getCollectionDescriptor().getElementDescriptor()
                        .getComponentContract());
            if (collectionAccessor instanceof IModelDescriptorAware) {
              ((IModelDescriptorAware) collectionAccessor)
                  .setModelDescriptor(reversePropertyDescriptor);
            }
            collectionAccessor.removeFromValue(value, proxy);
          }
        }
        Collection<?> oldCollectionSnapshot = CollectionHelper
            .cloneCollection((Collection<?>) collectionProperty);
        if (collectionProperty.remove(value)) {
          firePropertyChange(propertyName, oldCollectionSnapshot,
              collectionProperty);
          if (propertyProcessorsEnabled) {
            propertyDescriptor.postprocessRemover(proxy, collectionProperty,
                value);
          }
          if (proxy instanceof IEntity && value instanceof IEntity) {
            entityDetached((IEntity) value);
          }
        }
      }
    } catch (RuntimeException ex) {
      rollbackProperty(proxy, propertyDescriptor, collectionProperty);
      throw ex;
    } catch (InvocationTargetException ex) {
      rollbackProperty(proxy, propertyDescriptor, collectionProperty);
      if (ex.getCause() instanceof RuntimeException) {
        throw (RuntimeException) ex.getCause();
      }
      throw new ComponentException(ex.getCause());
    } catch (IllegalAccessException ex) {
      throw new ComponentException(ex);
    } catch (NoSuchMethodException ex) {
      throw new ComponentException(ex);
    }
  }

  private synchronized void removePropertyChangeListener(
      PropertyChangeListener listener) {
    if (listener == null || changeSupport == null) {
      return;
    }
    changeSupport.removePropertyChangeListener(listener);
  }

  private synchronized void removePropertyChangeListener(String propertyName,
      PropertyChangeListener listener) {
    if (listener == null || changeSupport == null) {
      return;
    }
    changeSupport.removePropertyChangeListener(propertyName, listener);
  }

  private void rollbackProperty(Object proxy,
      IPropertyDescriptor propertyDescriptor, Object oldProperty) {
    // boolean wasPropertyProcessorsEnabled = propertyProcessorsEnabled;
    // try {
    // propertyProcessorsEnabled = false;
    // setProperty(proxy, propertyDescriptor, oldProperty);
    // } finally {
    // propertyProcessorsEnabled = wasPropertyProcessorsEnabled;
    // }
    straightSetProperty(proxy, propertyDescriptor.getName(), oldProperty);
  }

  @SuppressWarnings("unchecked")
  private void setProperty(Object proxy,
      IPropertyDescriptor propertyDescriptor, Object newProperty) {
    String propertyName = propertyDescriptor.getName();

    Object oldProperty = null;
    try {
      oldProperty = accessorFactory.createPropertyAccessor(propertyName,
          componentDescriptor.getComponentContract()).getValue(proxy);
    } catch (IllegalAccessException ex) {
      throw new ComponentException(ex);
    } catch (InvocationTargetException ex) {
      if (ex.getCause() instanceof RuntimeException) {
        throw (RuntimeException) ex.getCause();
      }
      throw new ComponentException(ex.getCause());
    } catch (NoSuchMethodException ex) {
      throw new ComponentException(ex);
    }
    Object actualNewProperty;
    if (propertyProcessorsEnabled) {
      actualNewProperty = propertyDescriptor
          .interceptSetter(proxy, newProperty);
    } else {
      actualNewProperty = newProperty;
    }
    if (ObjectUtils.equals(oldProperty, actualNewProperty)) {
      return;
    }
    if (propertyProcessorsEnabled) {
      propertyDescriptor.preprocessSetter(proxy, actualNewProperty);
    }
    if (propertyDescriptor instanceof IRelationshipEndPropertyDescriptor) {
      // It's a relation end
      IRelationshipEndPropertyDescriptor reversePropertyDescriptor = ((IRelationshipEndPropertyDescriptor) propertyDescriptor)
          .getReverseRelationEnd();
      try {
        if (propertyDescriptor instanceof IReferencePropertyDescriptor) {
          // It's a 'one' relation end
          storeReferenceProperty(
              (IReferencePropertyDescriptor<?>) propertyDescriptor,
              oldProperty, actualNewProperty);
          if (reversePropertyDescriptor != null) {
            // It is bidirectionnal, so we are going to update the other end.
            if (reversePropertyDescriptor instanceof IReferencePropertyDescriptor) {
              // It's a one-to-one relationship
              if (oldProperty instanceof IEntity) {
                entityDetached((IEntity) oldProperty);
              }
              IAccessor reversePropertyAccessor = accessorFactory
                  .createPropertyAccessor(reversePropertyDescriptor.getName(),
                      ((IReferencePropertyDescriptor<?>) propertyDescriptor)
                          .getReferencedDescriptor().getComponentContract());
              if (oldProperty != null) {
                reversePropertyAccessor.setValue(oldProperty, null);
              }
              if (actualNewProperty != null) {
                reversePropertyAccessor.setValue(actualNewProperty, proxy);
              }
            } else if (reversePropertyDescriptor instanceof ICollectionPropertyDescriptor) {
              // It's a one-to-many relationship
              ICollectionAccessor reversePropertyAccessor = accessorFactory
                  .createCollectionPropertyAccessor(
                      reversePropertyDescriptor.getName(),
                      ((IReferencePropertyDescriptor<?>) propertyDescriptor)
                          .getReferencedDescriptor().getComponentContract(),
                      ((ICollectionPropertyDescriptor<?>) reversePropertyDescriptor)
                          .getCollectionDescriptor().getElementDescriptor()
                          .getComponentContract());
              if (reversePropertyAccessor instanceof IModelDescriptorAware) {
                ((IModelDescriptorAware) reversePropertyAccessor)
                    .setModelDescriptor(reversePropertyDescriptor);
              }
              if (oldProperty != null) {
                reversePropertyAccessor.removeFromValue(oldProperty, proxy);
              }
              if (actualNewProperty != null) {
                reversePropertyAccessor.addToValue(actualNewProperty, proxy);
              }
            }
          }
        } else if (propertyDescriptor instanceof ICollectionPropertyDescriptor) {
          // It's a 'many' relation end
          Collection<Object> oldPropertyElementsToRemove = new HashSet<Object>();
          Collection<Object> newPropertyElementsToAdd = new HashSet<Object>();
          Collection<Object> propertyElementsToKeep = new HashSet<Object>();

          if (oldProperty != null) {
            oldPropertyElementsToRemove.addAll((Collection<?>) oldProperty);
            propertyElementsToKeep.addAll((Collection<?>) oldProperty);
          }
          if (actualNewProperty != null) {
            newPropertyElementsToAdd.addAll((Collection<?>) actualNewProperty);
          }
          propertyElementsToKeep.retainAll(newPropertyElementsToAdd);
          oldPropertyElementsToRemove.removeAll(propertyElementsToKeep);
          newPropertyElementsToAdd.removeAll(propertyElementsToKeep);
          ICollectionAccessor propertyAccessor = accessorFactory
              .createCollectionPropertyAccessor(propertyDescriptor.getName(),
                  componentDescriptor.getComponentContract(),
                  ((ICollectionPropertyDescriptor<?>) propertyDescriptor)
                      .getCollectionDescriptor().getElementDescriptor()
                      .getComponentContract());
          for (Object element : oldPropertyElementsToRemove) {
            propertyAccessor.removeFromValue(proxy, element);
          }
          for (Object element : newPropertyElementsToAdd) {
            propertyAccessor.addToValue(proxy, element);
          }
          // if the property is a list we may restore the element order and be
          // careful not to miss one...
          if (actualNewProperty instanceof List) {
            Collection<Object> currentProperty = (Collection<Object>) oldProperty;
            List<Object> snapshot = new ArrayList<Object>(currentProperty);
            if (currentProperty instanceof List) {
              // Just check that only order differs
              Set<Object> temp = new HashSet<Object>(currentProperty);
              temp.removeAll((List<?>) actualNewProperty);
              currentProperty.clear();
              currentProperty.addAll((List<?>) actualNewProperty);
              currentProperty.addAll(temp);
              oldProperty = snapshot;
            }
          }
        }
      } catch (RuntimeException ex) {
        rollbackProperty(proxy, propertyDescriptor, oldProperty);
        throw ex;
      } catch (InvocationTargetException ex) {
        rollbackProperty(proxy, propertyDescriptor, oldProperty);
        if (ex.getCause() instanceof RuntimeException) {
          throw (RuntimeException) ex.getCause();
        }
        throw new ComponentException(ex.getCause());
      } catch (IllegalAccessException ex) {
        throw new ComponentException(ex);
      } catch (NoSuchMethodException ex) {
        throw new ComponentException(ex);
      }
    } else {
      storeProperty(propertyName, actualNewProperty);
    }
    firePropertyChange(propertyName, oldProperty, actualNewProperty);
    if (propertyProcessorsEnabled) {
      propertyDescriptor.postprocessSetter(proxy, oldProperty,
          actualNewProperty);
    }
  }

  private void straightSetProperties(Object proxy,
      Map<String, Object> backendProperties) {
    for (Map.Entry<String, Object> propertyEntry : backendProperties.entrySet()) {
      straightSetProperty(proxy, propertyEntry.getKey(),
          propertyEntry.getValue());
    }
  }

  private String toString(Object proxy) {
    try {
      String toStringPropertyName = componentDescriptor.getToStringProperty();
      Object toStringValue = accessorFactory.createPropertyAccessor(
          toStringPropertyName, componentDescriptor.getComponentContract())
          .getValue(proxy);
      if (toStringValue == null) {
        return "";
      }
      return toStringValue.toString();
    } catch (IllegalAccessException ex) {
      throw new ComponentException(ex);
    } catch (InvocationTargetException ex) {
      if (ex.getCause() instanceof RuntimeException) {
        throw (RuntimeException) ex.getCause();
      }
      throw new ComponentException(ex.getCause());
    } catch (NoSuchMethodException ex) {
      throw new ComponentException(ex);
    }
  }

  private class InlineReferenceTracker implements PropertyChangeListener {

    private String  componentName;
    private boolean enabled;
    private boolean inlinedComponent;

    /**
     * Constructs a new <code>InnerComponentTracker</code> instance.
     *
     * @param componentName
     *          the name of the component to track the properties.
     * @param inlinedComponent
     *          is it tracking an inlined component or an entity ref ?
     */
    public InlineReferenceTracker(String componentName, boolean inlinedComponent) {
      this.componentName = componentName;
      this.inlinedComponent = inlinedComponent;
      this.enabled = true;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void propertyChange(PropertyChangeEvent evt) {
      if (enabled) {
        boolean wasEnabled = enabled;
        String nestedPropertyName = componentName + "." + evt.getPropertyName();
        try {
          enabled = false;
          if (inlinedComponent) {
            // for dirtyness notification
            firePropertyChange(componentName, null, evt.getSource());
          }
          // for ui notification
          if (changeSupport != null
              && changeSupport.hasListeners(nestedPropertyName)) {
            firePropertyChange(nestedPropertyName, evt.getOldValue(),
                evt.getNewValue());
          }
        } finally {
          enabled = wasEnabled;
        }
      }
    }
  }

  private static final class NeverEqualsInvocationHandler implements
      InvocationHandler {

    private Object delegate;

    private NeverEqualsInvocationHandler(Object delegate) {
      this.delegate = delegate;
    }

    /**
     * Just 'overrides' the equals method to always return false.
     * <p>
     * {@inheritDoc}
     */
    @Override
    public Object invoke(@SuppressWarnings("unused") Object proxy,
        Method method, Object[] args) throws Throwable {
      if (method.getName().equals("equals") && args.length == 1) {
        return new Boolean(false);
      }
      return method.invoke(delegate, args);
    }
  }
}
TOP

Related Classes of org.jspresso.framework.model.component.basic.AbstractComponentInvocationHandler

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.