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.PermissionLevel;
import com.almworks.jira.structure.api.StructureException;
import com.almworks.jira.structure.api.StructureManager;
import com.almworks.jira.structure.api.StructureServices;
import com.almworks.jira.structure.api.forest.Forest;
import com.almworks.jira.structure.util.StructureUtil;
import com.atlassian.crowd.embedded.api.User;
import com.atlassian.jira.component.ComponentAccessor;
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.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
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
*/
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
   * @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) {
    try {
      return myStructureManager.getForest(instance.getStructureId(), getCurrentUser(), false);
    } catch (StructureException e) {
      logger.warn(this + "(" + instance + ") cannot resync " + instance.getStructureId(), e);
      return null;
    }
  }

  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(this + " could not sync structure " + structureId + ": user "  + getCurrentUser() + " does not have edit permissions");
    }
    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
   */
  @NotNull
  public static String issueDebug(@Nullable Long issue) {
    if (issue == null) return "null";
    StringBuilder r = new StringBuilder("(");
    MutableIssue issueObject = null;
    try {
      issueObject = ComponentAccessor.getIssueManager().getIssueObject(issue);
    } catch (Exception e) {
// ignore e
    }
    if (issueObject != null) {
      r.append(issueObject.getKey()).append(' ');
    }
    r.append(issue);
    r.append(')');
    return r.toString();
  }
}
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.