Package com.almworks.jira.structure.api.sync

Source Code of com.almworks.jira.structure.api.sync.AbstractSynchronizer

package com.almworks.jira.structure.api.sync;

import com.almworks.jira.structure.api.*;
import com.almworks.jira.structure.api.forest.Forest;
import com.almworks.jira.structure.util.StructureUtil;
import com.almworks.jira.structure.util.SyncLogger;
import com.atlassian.annotations.PublicSpi;
import com.atlassian.crowd.embedded.api.User;
import com.atlassian.jira.issue.IssueManager;
import com.atlassian.jira.issue.MutableIssue;
import com.atlassian.jira.security.JiraAuthenticationContext;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.xml.bind.*;
import java.io.*;
import java.util.Map;

/**
* <p><code>AbstractSynchronizer</code> is an abstract base class for the synchronizers that
* provides basic implementation for some of the {@link StructureSynchronizer} methods
* and offers some utility methods for the synchronizers.</p>
*
* <p>If your synchronizer needs to listen to JIRA issue events, you probably need
* to extend {@link AbstractIssueListeningSynchronizer} instead of this class.</p>
*
* <p>The type parameter for this class is the actual type of the parameters used
* by this sycnhronizer. The basic implementation of the {@link #storeParameters}
* and {@link #restoreParameters} methods serialize and deserialize <code>P</code>
* using JAXB, expecting that the parameters type are properly annotated (see
* {@link javax.xml.bind.annotation.XmlRootElement}).</p>
*
* @author Igor Sereda
*/
@PublicSpi
public abstract class AbstractSynchronizer<P> implements StructureSynchronizer {
  private static final Logger logger = LoggerFactory.getLogger(AbstractSynchronizer.class);

  protected final StructureServices myStructureServices;
  protected final StructureManager myStructureManager;
  protected final JiraAuthenticationContext myAuthenticationContext;
  protected final IssueManager myIssueManager;
  protected final SynchronizerUndoRecorder myUndoRecorder;

  private StructureSynchronizerModuleDescriptor myDescriptor;
  private final Class<P> myParametersClass;

  /**
   * Constructs an instance of the synchronizer.
   *
   * @param structureServices services facade
   * @param parametersClass parameters class
   */
  protected AbstractSynchronizer(StructureServices structureServices, Class<P> parametersClass) {
    myStructureServices = structureServices;
    myParametersClass = parametersClass;
    myStructureManager = structureServices.getStructureManager();
    myAuthenticationContext = structureServices.getAuthenticationContext();
    myIssueManager = structureServices.getIssueManager();
    myUndoRecorder = structureServices.getSynchronizerUndoRecorder();
  }

  /**
   * @return the expected class of the parameters
   */
  public Class<P> getParametersClass() {
    return myParametersClass;
  }

  /**
   * Called by the module descriptor on initialization
   *
   * @param descriptor descriptor for this module
   */
  public synchronized void init(StructureSynchronizerModuleDescriptor descriptor) {
    myDescriptor = descriptor;
  }

  /**
   * @return module descriptor, which can be used to retrieve configuration for this synchronizer
   * from the atlassian-plugin.xml
   */
  @NotNull
  public synchronized StructureSynchronizerModuleDescriptor getDescriptor() {
    return myDescriptor;
  }

  /**
   * Looks up i18n text using the i18n bean from the module's plugin and the current
   * user's locale.
   *
   * @param key text key
   * @param parameters optional parameters
   * @return the text or the key, if not found
   */
  @NotNull
  protected String getText(@NotNull String key, Object... parameters) {
    return getDescriptor().getI18nBean().getText(key, parameters);
  }

  /**
   * <p>Casts the parameters object passed from outside to the expected parameters class.</p>
   *
   * <p>If object class does not match, logs a warning and returns null.</p>
   *
   * @param p the parameters object
   * @return <code>p</code> cast to <code>P</code>
   */
  @Nullable
  protected P castParameters(Object p) {
    if (p == null) return null;
    if (!myParametersClass.isInstance(p)) {
      logger.warn(this + ": params of class " + p.getClass().getName() + " are not acceptable");
      return null;
    }
    return myParametersClass.cast(p);
  }

  public void start(@NotNull SyncInstance instance, @NotNull SyncController controller) {
  }

  public void stop(@NotNull SyncInstance instance) {
  }

  public void addDefaultFormParameters(@NotNull Map<String, Object> params) {
  }

  /**
   * Returns the current forest of the structure that this sync instance refers.
   *
   * @param instance the sync instance
   * @param log {@link SyncLogger logging helper} that will be used to log warning in case forest cannot be obtained.
   *   You can specify SyncLogger that you are already using in your synchronizer to write accurate sync mode to the logs (autosync or resync)                                           
   * @return the forest or <code>null</code> if the structure is not present or running user has no access to it
   */
  @Nullable
  protected Forest getSourceForest(@NotNull SyncInstance instance, @NotNull SyncLogger log) {
    try {
      return myStructureManager.getForest(instance.getStructureId(), getCurrentUser(), false);
    } catch (StructureException e) {
      if (e.getError() == StructureError.STRUCTURE_NOT_EXISTS_OR_NOT_ACCESSIBLE) log.warnStructureException(e);
      else log.warnExceptionIfDebug(e, "could not run");
      return null;
    }
  }

  /**
   * Returns the current forest of the structure that this sync instance refers.
   *
   * @param instance the sync instance
   * @return the forest or <code>null</code> if the structure is not present or running user has no access to it
   */
  @Nullable
  protected Forest getSourceForest(@NotNull SyncInstance instance) {
    return getSourceForest(instance, new SyncLogger(logger, instance, myStructureManager, false));
  }

  public byte[] storeParameters(Object parameters) throws IOException {
    if (parameters == null) return null;
    try {
      JAXBContext jaxbContext = StructureUtil.createJAXBContext(myParametersClass);
      if (jaxbContext == null) throw new IOException("cannot create JAXB context for " + myParametersClass);
      Marshaller marshaller = jaxbContext.createMarshaller();
      ByteArrayOutputStream out = new ByteArrayOutputStream();
//      marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
      marshaller.marshal(parameters, out);
      return out.toByteArray();
    } catch (JAXBException e) {
      logger.error("cannot serialize parameters", e);
      throw new IOException("cannot serialize parameters " + parameters, e);
    }
  }

  public P restoreParameters(byte[] data) throws IOException {
    if (data == null) return null;
    try {
      JAXBContext jaxbContext = StructureUtil.createJAXBContext(myParametersClass);
      if (jaxbContext == null) throw new IOException("cannot create JAXB context for " + myParametersClass);
      Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
      Object object = unmarshaller.unmarshal(new ByteArrayInputStream(data));
      if (object == null) return null;
      if (!myParametersClass.isInstance(object)) {
        logger.warn("cannot unmarshal synchronizer parameters, unexpected class " + object.getClass());
        return null;
      }
      return (P) object;
    } catch (JAXBException e) {
      throw new IOException("error unmarshalling parameters", e);
    }
  }

  /**
   * Checks that the user has at least {@link PermissionLevel#EDIT} permission on the specified structure.
   *
   * @param structureId the ID of the structure
   * @return true if the current user is allowed to modify the structure
   */
  protected boolean verifyStructureEditPermissions(long structureId) {
    boolean r = myStructureManager.isAccessible(structureId, getCurrentUser(), PermissionLevel.EDIT, false);
    if (!r) {
      logger.warn("{} cannot run under user {} because he or she does not have permissions to edit structure {}", new Object[]{this, StructureUtil.username(getCurrentUser()), structureId});
    }
    return r;
  }
    /**
    * Checks that the user has at least {@link PermissionLevel#EDIT} permission on the specified structure.
    *
    * @param structureId the ID of the structure
    * @param log {@link SyncLogger logging helper} that will be used to log warning in case the structure does not exist or is not accessible;
    *  in case you don't need synchronizer information in the logs, you can use {@link #verifyStructureEditPermissions(long)}                                           
    * @return true if the current user is allowed to modify the structure
    */
  protected boolean verifyStructureEditPermissions(long structureId, SyncLogger log) {
    boolean r = myStructureManager.isAccessible(structureId, getCurrentUser(), PermissionLevel.EDIT, false);
    if (!r) {
      log.warn("cannot run under user", StructureUtil.username(getCurrentUser()), "because he or she does not have permissions to edit the structure");
    }
    return r;
  }

  private User getCurrentUser() {
    return myAuthenticationContext.getLoggedInUser();
  }

  /**
   * Retrieves an instance of <code>Issue</code>.
   *
   * @param issueId the ID of the issue
   * @return the issue, or null if the issue cannot be found or there is an exception getting it
   */
  @Nullable
  protected MutableIssue getIssue(long issueId) {
    MutableIssue issueObject = null;
    try {
      issueObject = myIssueManager.getIssueObject(issueId);
    } catch (Exception e) {
      logger.warn("cannot retrieve issue " + issueId + ": " + e);
    }
    return issueObject;
  }

  /**
   * Retrieves an instance of issue by issue key.
   *
   * @param key issue key
   * @return the issue, or null if the issue cannot be found or there is an exception getting it
   */
  @Nullable
  protected MutableIssue getIssue(@NotNull String key) {
    MutableIssue issueObject = null;
    try {
      issueObject = myIssueManager.getIssueObject(key);
    } catch (Exception e) {
      logger.warn("cannot retrieve issue " + key + ": " + e);
    }
    return issueObject;
  }


  /**
   * Returns a string representation of the issue that is used to write log messages. Writes issue ID and, if possible,
   * issue key.

   * @param issue the ID of the issue
   * @return string that can be used in output
   *
   * @deprecated use {@link StructureUtil#getDebugIssueString(Long)}
   */
  @Deprecated
  @NotNull
  public static String issueDebug(@Nullable Long issue) {
    return StructureUtil.getDebugIssueString(issue);
  }
}
TOP

Related Classes of com.almworks.jira.structure.api.sync.AbstractSynchronizer

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.