Example usage for java.text DateFormat getDateTimeInstance

List of usage examples for java.text DateFormat getDateTimeInstance

Introduction

In this page you can find the example usage for java.text DateFormat getDateTimeInstance.

Prototype

public static final DateFormat getDateTimeInstance() 

Source Link

Document

Gets the date/time formatter with the default formatting style for the default java.util.Locale.Category#FORMAT FORMAT locale.

Usage

From source file:org.archive.crawler.extractor.ExtractorHTML.java

public void extract(CrawlURI curi) {
    if (!isHttpTransactionContentToProcess(curi) || !(isExpectedMimeType(curi.getContentType(), "text/html")
            || isExpectedMimeType(curi.getContentType(), "application/xhtml")
            || isExpectedMimeType(curi.getContentType(), "text/vnd.wap.wml")
            || isExpectedMimeType(curi.getContentType(), "application/vnd.wap.wml")
            || isExpectedMimeType(curi.getContentType(), "application/vnd.wap.xhtml"))) {
        return;//from   w ww.ja va2s  . co m
    }

    final boolean ignoreUnexpectedHTML = ((Boolean) getUncheckedAttribute(curi, ATTR_IGNORE_UNEXPECTED_HTML))
            .booleanValue();

    if (ignoreUnexpectedHTML) {
        try {
            if (!isHtmlExpectedHere(curi)) {
                // HTML was not expected (eg a GIF was expected) so ignore
                // (as if a soft 404)
                return;
            }
        } catch (URIException e) {
            logger.severe("Failed expectedHTML test: " + e.getMessage());
        }
    }

    this.numberOfCURIsHandled++;

    ReplayCharSequence cs = null;

    String codecharset = "utf-8";

    try {
        HttpRecorder hr = curi.getHttpRecorder();
        if (hr == null) {
            throw new IOException("Why is recorder null here?");
        }
        cs = hr.getReplayCharSequence();
        // 
        codecharset = hr.getCharacterEncoding();
        /*
         * { writer_c = new BufferedWriter(new FileWriter( new
         * File("D:\\code.txt"), true)); writer_c.write(curi.toString() +
         * ":  " + codecharset + "\r\n"); writer_c.close(); }
         */

    } catch (IOException e) {
        curi.addLocalizedError(this.getName(), e,
                "Failed get of replay char sequence " + curi.toString() + " " + e.getMessage());
        logger.log(Level.SEVERE, "Failed get of replay char sequence in " + Thread.currentThread().getName(),
                e);
    }

    if (cs == null) {
        return;
    }

    // We have a ReplayCharSequence open. Wrap all in finally so we
    // for sure close it before we leave.
    try {
        // Extract all links from the charsequence
        extract(curi, cs);

        // 
        {
            System.out.println(curi.toString() + "   Processing");
            try {
                String uri = curi.toString();
                String temurl = uri.substring(7);
                temurl = temurl.replaceAll("[\\?/:*|<>\"]", "_");
                temurl = temurl.replaceAll("#|%|@|&|!|~|'", "11");
                if (temurl.length() > 30)
                    temurl = temurl.substring(0, 30);
                xmlReader xmlreader = new xmlReader();
                //File filenameo = new File("D:/Work/WEB app/SearchEngineServer/crawler/dowloaded_files/" + DateFormat.getDateInstance().format(new Date()));
                File filenameo = new File(xmlreader.getCrawlerPath("download"));
                filenameo.mkdirs();
                String file_path = filenameo.getAbsolutePath() + "/" + temurl + ".html";
                File filename = new File(file_path);
                if (!filename.exists()) {
                    filename.createNewFile();
                    System.out.println("Created:" + "  " + curi.toString() + ".html");
                    OutputStreamWriter write = new OutputStreamWriter(new FileOutputStream(filename),
                            codecharset);
                    BufferedWriter writer = new BufferedWriter(write);
                    writer.write(uri + "\r\n");
                    writer.write(DateFormat.getDateTimeInstance().format(new Date()) + "\r\n");
                    writer.write(codecharset + "\r\n");
                    writer.write("<begin!>\r\n");
                    String content = cs.toString();
                    writer.write(content);
                    writer.close();
                }
            } catch (Exception e) {
            }
        }
        // Set flag to indicate that link extraction is completed.
        curi.linkExtractorFinished();
    } finally {
        if (cs != null) {
            try {
                cs.close();
            } catch (IOException ioe) {
                logger.warning(TextUtils.exceptionToString("Failed close of ReplayCharSequence.", ioe));
            }
        }
    }
}

From source file:org.apache.wiki.auth.user.XMLUserDatabase.java

/**
 * After loading the DOM, this method sanity-checks the dates in the DOM and makes
 * sure they are formatted properly. This is sort-of hacky, but it should work.
 *///from w  w  w.ja  v  a  2  s. c  om
private void sanitizeDOM() {
    if (c_dom == null) {
        throw new IllegalStateException("FATAL: database does not exist");
    }

    NodeList users = c_dom.getElementsByTagName(USER_TAG);
    for (int i = 0; i < users.getLength(); i++) {
        Element user = (Element) users.item(i);

        // Sanitize UID (and generate a new one if one does not exist)
        String uid = user.getAttribute(UID).trim();
        if (uid == null || uid.length() == 0 || "-1".equals(uid)) {
            uid = String.valueOf(generateUid(this));
            user.setAttribute(UID, uid);
        }

        // Sanitize dates
        String loginName = user.getAttribute(LOGIN_NAME);
        String created = user.getAttribute(CREATED);
        String modified = user.getAttribute(LAST_MODIFIED);
        DateFormat c_format = new SimpleDateFormat(DATE_FORMAT);
        try {
            created = c_format.format(c_format.parse(created));
            modified = c_format.format(c_format.parse(modified));
            user.setAttribute(CREATED, created);
            user.setAttribute(LAST_MODIFIED, modified);
        } catch (ParseException e) {
            try {
                created = c_format.format(DateFormat.getDateTimeInstance().parse(created));
                modified = c_format.format(DateFormat.getDateTimeInstance().parse(modified));
                user.setAttribute(CREATED, created);
                user.setAttribute(LAST_MODIFIED, modified);
            } catch (ParseException e2) {
                log.warn("Could not parse 'created' or 'lastModified' attribute for profile '" + loginName
                        + "'." + " It may have been tampered with.");
            }
        }
    }
}

From source file:edu.harvard.i2b2.eclipse.LoginView.java

/**
 * get current date as string used for logi
 * /*from  ww  w . j a v  a 2s . c om*/
 * @return
 */
public static String getNow() {
    return DateFormat.getDateTimeInstance().format(new Date());
}

From source file:org.pengyou.ooo.PublishAsDialog.java

private String formatList(String[] t) {
    String res = new String();

    if (t[2] == "COLLECTION") {
        if (t[4].length() > 21)
            res = t[4].substring(0, 20) + "";
        else//from   w ww  .j ava 2  s .  c om
            res = t[4];
        //res = " " + res; 
        res = " " + res;
    } else {
        if (t[4].length() > 23)
            res = t[4].substring(0, 22) + "";
        else
            res = t[4];
    }

    //int l = res.length();
    while (res.length() < 25)
        res += " ";

    res += t[6];
    while (res.length() < 42)
        res += " ";

    Locale locale = Locale.FRENCH;
    Date date = null;
    String s = null;

    try {
        date = DateFormat.getDateTimeInstance().parse(t[5]);
        s = DateFormat.getDateInstance(DateFormat.MEDIUM, locale).format(date);
        res += s;
        while (res.length() < 57)
            res += " ";
    } catch (ParseException e) {
        e.printStackTrace();
        log.log(Level.DEBUG, e.getLocalizedMessage());
    }
    date = null;
    s = null;
    try {
        date = DateFormat.getDateTimeInstance().parse(t[3]);
        s = DateFormat.getDateInstance(DateFormat.MEDIUM, locale).format(date);
        res += s;
        while (res.length() < 71)
            res += " ";
    } catch (ParseException e) {
        e.printStackTrace();
        log.log(Level.DEBUG, e.getLocalizedMessage());
    }

    return res;
}

From source file:de.knowwe.defi.usermanager.XMLUserDatabase.java

/**
 * After loading the DOM, this method sanity-checks the dates in the DOM and makes
 * sure they are formatted properly. This is sort-of hacky, but it should work.
 *///ww  w.j a va  2s  . co m
private void sanitizeDOM() {
    if (c_dom == null) {
        throw new IllegalStateException("FATAL: database does not exist");
    }

    NodeList users = c_dom.getElementsByTagName(USER_TAG);
    for (int i = 0; i < users.getLength(); i++) {
        Element user = (Element) users.item(i);

        // Sanitize UID (and generate a new one if one does not exist)
        String uid = user.getAttribute(UID).trim();
        if (uid == null || uid.isEmpty() || "-1".equals(uid)) {
            uid = String.valueOf(generateUid(this));
            user.setAttribute(UID, uid);
        }

        // Sanitize dates
        String loginName = user.getAttribute(LOGIN_NAME);
        String created = user.getAttribute(CREATED);
        String modified = user.getAttribute(LAST_MODIFIED);
        DateFormat c_format = new SimpleDateFormat(DATE_FORMAT);
        try {
            created = c_format.format(c_format.parse(created));
            modified = c_format.format(c_format.parse(modified));
            user.setAttribute(CREATED, created);
            user.setAttribute(LAST_MODIFIED, modified);
        } catch (ParseException e) {
            try {
                created = c_format.format(DateFormat.getDateTimeInstance().parse(created));
                modified = c_format.format(DateFormat.getDateTimeInstance().parse(modified));
                user.setAttribute(CREATED, created);
                user.setAttribute(LAST_MODIFIED, modified);
            } catch (ParseException e2) {
                Log.warning("Could not parse 'created' or 'lastModified' attribute for profile '" + loginName
                        + "'." + " It may have been tampered with.");
            }
        }
    }
}

From source file:com.irccloud.android.activity.MainActivity.java

private void show_topic_popup() {
    if (buffer == null)
        return;//from ww  w .  j  a  v a2 s.c  om
    ChannelsDataSource.Channel c = ChannelsDataSource.getInstance().getChannelForBuffer(buffer.bid);
    if (c != null) {
        AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this);
        builder.setInverseBackgroundForced(Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB);
        View v = getLayoutInflater().inflate(R.layout.dialog_topic, null);
        if (c.topic_text.length() > 0) {
            String author = "";
            if (c.topic_author != null && c.topic_author.length() > 0) {
                author = " Set by " + c.topic_author;
                if (c.topic_time > 0) {
                    author += " on " + DateFormat.getDateTimeInstance().format(new Date(c.topic_time * 1000));
                }
                v.findViewById(R.id.setBy).setVisibility(View.VISIBLE);
                ((TextView) v.findViewById(R.id.setBy)).setText(author);
            }
            ((TextView) v.findViewById(R.id.topic)).setText(ColorFormatter.html_to_spanned(
                    ColorFormatter.emojify(ColorFormatter.irc_to_html(TextUtils.htmlEncode(c.topic_text))),
                    true, server));
        } else {
            ((TextView) v.findViewById(R.id.topic)).setText("No topic set.");
        }
        if (c.mode.length() > 0) {
            v.findViewById(R.id.mode).setVisibility(View.VISIBLE);
            ((TextView) v.findViewById(R.id.mode)).setText("Mode: +" + c.mode);

            for (ChannelsDataSource.Mode m : c.modes) {
                switch (m.mode) {
                case "i":
                    v.findViewById(R.id.mode_i).setVisibility(View.VISIBLE);
                    break;
                case "k":
                    v.findViewById(R.id.mode_k).setVisibility(View.VISIBLE);
                    ((TextView) v.findViewById(R.id.key)).setText(m.param);
                    break;
                case "m":
                    v.findViewById(R.id.mode_m).setVisibility(View.VISIBLE);
                    break;
                case "n":
                    v.findViewById(R.id.mode_n).setVisibility(View.VISIBLE);
                    break;
                case "p":
                    v.findViewById(R.id.mode_p).setVisibility(View.VISIBLE);
                    break;
                case "s":
                    v.findViewById(R.id.mode_s).setVisibility(View.VISIBLE);
                    break;
                case "t":
                    v.findViewById(R.id.mode_t).setVisibility(View.VISIBLE);
                    break;
                }
            }
        }
        builder.setView(v);
        builder.setNegativeButton("Close", new DialogInterface.OnClickListener() {

            @Override
            public void onClick(DialogInterface dialog, int which) {
                dialog.dismiss();
            }
        });
        boolean canEditTopic;
        if (c.hasMode("t")) {
            UsersDataSource.User self_user = UsersDataSource.getInstance().getUser(buffer.bid, server.nick);
            canEditTopic = (self_user != null
                    && (self_user.mode.contains(server != null ? server.MODE_OPER : "Y")
                            || self_user.mode.contains(server != null ? server.MODE_OWNER : "q")
                            || self_user.mode.contains(server != null ? server.MODE_ADMIN : "a")
                            || self_user.mode.contains(server != null ? server.MODE_OP : "o")
                            || self_user.mode.contains(server != null ? server.MODE_HALFOP : "h")));
        } else {
            canEditTopic = true;
        }

        if (canEditTopic) {
            builder.setPositiveButton("Edit Topic", new DialogInterface.OnClickListener() {

                @Override
                public void onClick(DialogInterface dialog, int which) {
                    dialog.dismiss();
                    editTopic();
                }
            });
        }
        final AlertDialog dialog = builder.create();
        dialog.setOwnerActivity(MainActivity.this);
        dialog.show();

        ((TextView) v.findViewById(R.id.topic)).setMovementMethod(new LinkMovementMethod() {
            @Override
            public boolean onTouchEvent(TextView widget, Spannable buffer, MotionEvent event) {
                if (super.onTouchEvent(widget, buffer, event) && event.getAction() == MotionEvent.ACTION_UP) {
                    dialog.dismiss();
                    return true;
                }
                return false;
            }
        });
    }
}

From source file:com.javielinux.utils.Utils.java

public static String timeFromTweetExtended(Context cnt, Date timeTweet) {

    if (Integer.parseInt(Utils.getPreference(cnt).getString("prf_date_format", "1")) == 1) {
        if (timeTweet != null) {
            Date now = new Date();

            long diff = ((now.getTime() - timeTweet.getTime())) / 1000;
            String out = "";

            if (diff < 60) {
                out = String.valueOf(diff) + " " + cnt.getString(R.string.seconds);
            } else {
                // minutos
                long sec = diff % 60;
                diff = diff / 60;//from  w  w  w  . ja va 2  s  .com
                if (diff < 60) {
                    out = String.valueOf(diff) + " " + cnt.getString(R.string.minutes) + " "
                            + cnt.getString(R.string.and) + " " + sec + " " + cnt.getString(R.string.seconds);
                } else {
                    // horas
                    long min = diff % 60;
                    diff = diff / 60;
                    if (diff < 24) {
                        out = String.valueOf(diff) + " " + cnt.getString(R.string.hours) + " "
                                + cnt.getString(R.string.and) + " " + min + " "
                                + cnt.getString(R.string.minutes);
                    } else {
                        // dias
                        long hours = diff % 24;
                        diff = diff / 24;
                        out = String.valueOf(diff) + " " + cnt.getString(R.string.days) + " "
                                + cnt.getString(R.string.and) + " " + hours + " "
                                + cnt.getString(R.string.hours);
                    }
                }
            }

            return cnt.getString(R.string.date_long, out);
        }
    } else {
        return DateFormat.getDateTimeInstance().format(timeTweet);
    }
    return "";
}

From source file:org.opendatakit.briefcase.util.ExportToCsv.java

private boolean processInstance(File instanceDir) {
    File submission = new File(instanceDir, "submission.xml");
    if (!submission.exists() || !submission.isFile()) {
        EventBus.publish(new ExportProgressEvent(
                "Submission not found for instance directory: " + instanceDir.getPath()));
        return false;
    }//from  www.  jav  a  2  s.c  om
    EventBus.publish(new ExportProgressEvent("Processing instance: " + instanceDir.getName()));

    // If we are encrypted, be sure the temporary directory
    // that will hold the unencrypted files is created and empty.
    // If we aren't encrypted, the temporary directory
    // is the same as the instance directory.

    File unEncryptedDir;
    if (briefcaseLfd.isFileEncryptedForm()) {
        // create or clean-up the temp directory that will hold the unencrypted
        // files. Do this in the outputDir so that the briefcase storage location
        // can be a read-only network mount. issue 676.
        unEncryptedDir = new File(outputDir, ".temp");

        if (unEncryptedDir.exists()) {
            // silently delete it...
            try {
                FileUtils.deleteDirectory(unEncryptedDir);
            } catch (IOException e) {
                e.printStackTrace();
                EventBus.publish(new ExportProgressEvent(
                        "Unable to delete stale temp directory: " + unEncryptedDir.getAbsolutePath()));
                return false;
            }
        }

        if (!unEncryptedDir.mkdirs()) {
            EventBus.publish(new ExportProgressEvent(
                    "Unable to create temp directory: " + unEncryptedDir.getAbsolutePath()));
            return false;
        }
    } else {
        unEncryptedDir = instanceDir;
    }

    // parse the xml document (this is the manifest if encrypted)...
    Document doc;
    boolean isValidated = false;

    try {
        doc = XmlManipulationUtils.parseXml(submission);
    } catch (ParsingException e) {
        e.printStackTrace();
        EventBus.publish(new ExportProgressEvent(
                "Error parsing submission " + instanceDir.getName() + " Cause: " + e.toString()));
        return false;
    } catch (FileSystemException e) {
        e.printStackTrace();
        EventBus.publish(new ExportProgressEvent(
                "Error parsing submission " + instanceDir.getName() + " Cause: " + e.toString()));
        return false;
    }

    String submissionDate = null;
    // extract the submissionDate, if present, from the attributes
    // of the root element of the submission or submission manifest (if encrypted).
    submissionDate = doc.getRootElement().getAttributeValue(null, "submissionDate");
    if (submissionDate == null || submissionDate.length() == 0) {
        submissionDate = null;
    } else {
        Date theDate = WebUtils.parseDate(submissionDate);
        DateFormat formatter = DateFormat.getDateTimeInstance();
        submissionDate = formatter.format(theDate);

        // just return true to skip records out of range
        if (startDate != null && theDate.before(startDate)) {
            log.info("Submission date is before specified, skipping: " + instanceDir.getName());
            return true;
        }
        if (endDate != null && theDate.after(endDate)) {
            log.info("Submission date is after specified, skipping: " + instanceDir.getName());
            return true;
        }
        // don't export records without dates if either date is set
        if ((startDate != null || endDate != null) && submissionDate == null) {
            log.info("No submission date found, skipping: " + instanceDir.getName());
            return true;
        }
    }

    // Beyond this point, we need to have a finally block that
    // will clean up any decrypted files whenever there is any
    // failure.
    try {

        if (briefcaseLfd.isFileEncryptedForm()) {
            // Decrypt the form and all its media files into the
            // unEncryptedDir and validate the contents of all
            // those files.
            // NOTE: this changes the value of 'doc'
            try {
                FileSystemUtils.DecryptOutcome outcome = FileSystemUtils.decryptAndValidateSubmission(doc,
                        briefcaseLfd.getPrivateKey(), instanceDir, unEncryptedDir);
                doc = outcome.submission;
                isValidated = outcome.isValidated;
            } catch (ParsingException e) {
                e.printStackTrace();
                EventBus.publish(new ExportProgressEvent(
                        "Error decrypting submission " + instanceDir.getName() + " Cause: " + e.toString()));
                return false;
            } catch (FileSystemException e) {
                e.printStackTrace();
                EventBus.publish(new ExportProgressEvent(
                        "Error decrypting submission " + instanceDir.getName() + " Cause: " + e.toString()));
                return false;
            } catch (CryptoException e) {
                e.printStackTrace();
                EventBus.publish(new ExportProgressEvent(
                        "Error decrypting submission " + instanceDir.getName() + " Cause: " + e.toString()));
                return false;
            }
        }

        String instanceId = null;
        String base64EncryptedFieldKey = null;
        // find an instanceId to use...
        try {
            FormInstanceMetadata sim = XmlManipulationUtils.getFormInstanceMetadata(doc.getRootElement());
            instanceId = sim.instanceId;
            base64EncryptedFieldKey = sim.base64EncryptedFieldKey;
        } catch (ParsingException e) {
            e.printStackTrace();
            EventBus.publish(new ExportProgressEvent("Could not extract metadata from submission: "
                    + submission.getAbsolutePath() + " Cause: " + e.toString()));
            return false;
        }

        if (instanceId == null || instanceId.length() == 0) {
            // if we have no instanceID, and there isn't any in the file,
            // use the checksum as the id.
            // NOTE: encrypted submissions always have instanceIDs.
            // This is for legacy non-OpenRosa forms.
            long checksum;
            try {
                checksum = FileUtils.checksumCRC32(submission);
            } catch (IOException e1) {
                e1.printStackTrace();
                EventBus.publish(new ExportProgressEvent("Failed during computing of crc: " + e1.getMessage()));
                return false;
            }
            instanceId = "crc32:" + Long.toString(checksum);
        }

        if (terminationFuture.isCancelled()) {
            EventBus.publish(new ExportProgressEvent("ABORTED"));
            return false;
        }

        EncryptionInformation ei = null;
        if (base64EncryptedFieldKey != null) {
            try {
                ei = new EncryptionInformation(base64EncryptedFieldKey, instanceId,
                        briefcaseLfd.getPrivateKey());
            } catch (CryptoException e) {
                e.printStackTrace();
                EventBus.publish(new ExportProgressEvent("Error establishing field decryption for submission "
                        + instanceDir.getName() + " Cause: " + e.toString()));
                return false;
            }
        }

        // emit the csv record...
        try {
            OutputStreamWriter osw = fileMap.get(briefcaseLfd.getSubmissionElement());

            emitString(osw, true, submissionDate);
            emitSubmissionCsv(osw, ei, doc.getRootElement(), briefcaseLfd.getSubmissionElement(),
                    briefcaseLfd.getSubmissionElement(), false, instanceId, unEncryptedDir);
            emitString(osw, false, instanceId);
            if (briefcaseLfd.isFileEncryptedForm()) {
                emitString(osw, false, Boolean.toString(isValidated));
                if (!isValidated) {
                    EventBus.publish(new ExportProgressEvent("Decrypted submission " + instanceDir.getName()
                            + " may be missing attachments and could not be validated."));
                }
            }
            osw.append("\n");
            return true;

        } catch (IOException e) {
            e.printStackTrace();
            EventBus.publish(new ExportProgressEvent("Failed writing csv: " + e.getMessage()));
            return false;
        }
    } finally {
        if (briefcaseLfd.isFileEncryptedForm()) {
            // destroy the temp directory and its contents...
            try {
                FileUtils.deleteDirectory(unEncryptedDir);
            } catch (IOException e) {
                e.printStackTrace();
                EventBus.publish(
                        new ExportProgressEvent("Unable to remove decrypted files: " + e.getMessage()));
                return false;
            }
        }
    }
}

From source file:org.alfresco.repo.security.sync.TenantChainingUserRegistrySynchronizer.java

/**
 * Synchronizes local groups and users with a {@link UserRegistry} for a
 * particular zone, optionally handling deletions.
 * /*from w w w .  ja va 2 s. c  o  m*/
 * @param zone
 *            the zone id. This identifier is used to tag all created groups
 *            and users, so that in the future we can tell those that have
 *            been deleted from the registry.
 * @param userRegistry
 *            the user registry for the zone.
 * @param forceUpdate
 *            Should the complete set of users and groups be updated /
 *            created locally or just those known to have changed since the
 *            last sync? When <code>true</code> then <i>all</i> users and
 *            groups are queried from the user registry and updated locally.
 *            When <code>false</code> then each source is only queried for
 *            those users and groups modified since the most recent
 *            modification date of all the objects last queried from that
 *            same source.
 * @param isFullSync
 *            Should a complete set of user and group IDs be queried from
 *            the user registries in order to determine deletions? This
 *            parameter is independent of <code>force</code> as a separate
 *            query is run to process updates.
 * @param splitTxns
 *            Can the modifications to Alfresco be split across multiple
 *            transactions for maximum performance? If <code>true</code>,
 *            users and groups are created/updated in batches for increased
 *            performance. If <code>false</code>, all users and groups are
 *            processed in the current transaction. This is required if
 *            calling synchronously (e.g. in response to an authentication
 *            event in the same transaction).
 * @param visitedZoneIds
 *            the set of zone ids already processed. These zones have
 *            precedence over the current zone when it comes to group name
 *            'collisions'. If a user or group is queried that already
 *            exists locally but is tagged with one of the zones in this
 *            set, then it will be ignored as this zone has lower priority.
 * @param allZoneIds
 *            the set of all zone ids in the authentication chain. Helps us
 *            work out whether the zone information recorded against a user
 *            or group is invalid for the current authentication chain and
 *            whether the user or group needs to be 're-zoned'.
 */
private void syncWithPlugin(final String zone, UserRegistry userRegistry, boolean forceUpdate,
        boolean isFullSync, boolean splitTxns, final Set<String> visitedZoneIds, final Set<String> allZoneIds) {
    // Create a prefixed zone ID for use with the authority service
    final String zoneId = AuthorityService.ZONE_AUTH_EXT_PREFIX + zone;

    // Ensure that the zoneId exists before multiple threads start using it
    this.transactionService.getRetryingTransactionHelper()
            .doInTransaction(new RetryingTransactionCallback<Void>() {
                @Override
                public Void execute() throws Throwable {
                    authorityService.getOrCreateZone(zoneId);
                    return null;
                }
            }, false, splitTxns);

    // The set of zones we associate with new objects (default plus registry
    // specific)
    final Set<String> zoneSet = getZones(zoneId);

    long lastModifiedMillis = forceUpdate ? -1
            : getMostRecentUpdateTime(TenantChainingUserRegistrySynchronizer.GROUP_LAST_MODIFIED_ATTRIBUTE,
                    zoneId, splitTxns);
    Date lastModified = lastModifiedMillis == -1 ? null : new Date(lastModifiedMillis);

    if (TenantChainingUserRegistrySynchronizer.logger.isInfoEnabled()) {
        if (lastModified == null) {
            TenantChainingUserRegistrySynchronizer.logger
                    .info("Retrieving all groups from user registry '" + zone + "'");
        } else {
            TenantChainingUserRegistrySynchronizer.logger.info(
                    "Retrieving groups changed since " + DateFormat.getDateTimeInstance().format(lastModified)
                            + " from user registry '" + zone + "'");
        }
    }

    // First, analyze the group structure. Create maps of authorities to
    // their parents for associations to create
    // and delete. Also deal with 'overlaps' with other zones in the
    // authentication chain.
    final BatchProcessor<NodeDescription> groupProcessor = new BatchProcessor<NodeDescription>(
            zone + " Group Analysis", this.transactionService.getRetryingTransactionHelper(),
            userRegistry.getGroups(lastModified), this.workerThreads, 20, this.applicationEventPublisher,
            TenantChainingUserRegistrySynchronizer.logger, this.loggingInterval);
    class Analyzer extends BaseBatchProcessWorker<NodeDescription> {
        private final Map<String, String> groupsToCreate = new TreeMap<String, String>();
        private final Map<String, Set<String>> personParentAssocsToCreate = newPersonMap();
        private final Map<String, Set<String>> personParentAssocsToDelete = newPersonMap();
        private Map<String, Set<String>> groupParentAssocsToCreate = new TreeMap<String, Set<String>>();
        private final Map<String, Set<String>> groupParentAssocsToDelete = new TreeMap<String, Set<String>>();
        private final Map<String, Set<String>> finalGroupChildAssocs = new TreeMap<String, Set<String>>();
        private List<String> personsProcessed = new LinkedList<String>();
        private Set<String> allZonePersons = Collections.emptySet();
        private Set<String> deletionCandidates;

        private long latestTime;

        public Analyzer(final long latestTime) {
            this.latestTime = latestTime;
        }

        public long getLatestTime() {
            return this.latestTime;
        }

        public Set<String> getDeletionCandidates() {
            return this.deletionCandidates;
        }

        public String getIdentifier(NodeDescription entry) {
            return entry.getSourceId();
        }

        public void process(final NodeDescription group) throws Throwable {
            PropertyMap groupProperties = group.getProperties();
            String tenantDomain = (String) groupProperties.get(ContentModel.PROP_ORGANIZATION);
            logger.debug("Process group: " + groupProperties.get(ContentModel.PROP_AUTHORITY_NAME)
                    + (tenantDomain == null ? "" : ("/" + tenantDomain)));

            if (tenantDomain != null) {
                if (!isTenantEnabled(tenantDomain)) {
                    return;
                }
                AuthenticationUtil.runAs(new RunAsWork<Void>() {
                    @Override
                    public Void doWork() throws Exception {
                        processInTenantMode(group);
                        return null;
                    }
                }, SEIPTenantIntegration.getSystemUserByTenantId(tenantDomain));
            } else {
                processInTenantMode(group);
            }
        }

        private void processInTenantMode(NodeDescription group) {
            PropertyMap groupProperties = group.getProperties();
            String groupName = (String) groupProperties.get(ContentModel.PROP_AUTHORITY_NAME);
            String groupShortName = TenantChainingUserRegistrySynchronizer.this.authorityService
                    .getShortName(groupName);
            Set<String> groupZones = TenantChainingUserRegistrySynchronizer.this.authorityService
                    .getAuthorityZones(groupName);

            if (groupZones == null) {
                // The group did not exist at all
                updateGroup(group, false);
            } else {
                // Check whether the group is in any of the authentication
                // chain zones
                Set<String> intersection = new TreeSet<String>(groupZones);
                intersection.retainAll(allZoneIds);
                // Check whether the group is in any of the higher priority
                // authentication chain zones
                Set<String> visited = new TreeSet<String>(intersection);
                visited.retainAll(visitedZoneIds);

                if (groupZones.contains(zoneId)) {
                    // The group already existed in this zone: update the
                    // group
                    updateGroup(group, true);
                } else if (!visited.isEmpty()) {
                    // A group that exists in a different zone with higher
                    // precedence
                    return;
                } else if (!allowDeletions || intersection.isEmpty()) {
                    // Deletions are disallowed or the group exists, but not
                    // in a zone that's in the authentication
                    // chain. May be due to upgrade or zone changes. Let's
                    // re-zone them
                    if (TenantChainingUserRegistrySynchronizer.logger.isWarnEnabled()) {
                        TenantChainingUserRegistrySynchronizer.logger.warn("Updating group '" + groupShortName
                                + "'. This group will in future be assumed to originate from user registry '"
                                + zone + "'.");
                    }
                    updateAuthorityZones(groupName, groupZones, zoneSet);

                    // The group now exists in this zone: update the group
                    updateGroup(group, true);
                } else {
                    // The group existed, but in a zone with lower
                    // precedence
                    if (TenantChainingUserRegistrySynchronizer.logger.isWarnEnabled()) {
                        TenantChainingUserRegistrySynchronizer.logger.warn("Recreating occluded group '"
                                + groupShortName
                                + "'. This group was previously created through synchronization with a lower priority user registry.");
                    }
                    TenantChainingUserRegistrySynchronizer.this.authorityService.deleteAuthority(groupName);

                    // create the group
                    updateGroup(group, false);
                }
            }

            synchronized (this) {
                // Maintain the last modified date
                Date groupLastModified = group.getLastModified();
                if (groupLastModified != null) {
                    this.latestTime = Math.max(this.latestTime, groupLastModified.getTime());
                }
            }
        }

        // Recursively walks and caches the authorities relating to and from
        // this group so that we can later detect potential cycles
        private Set<String> getContainedAuthorities(String groupName) {
            // Return the cached children if it is processed
            Set<String> children = this.finalGroupChildAssocs.get(groupName);
            if (children != null) {
                return children;
            }

            // First, recurse to the parent most authorities
            for (String parent : TenantChainingUserRegistrySynchronizer.this.authorityService
                    .getContainingAuthorities(null, groupName, true)) {
                getContainedAuthorities(parent);
            }

            // Now descend on unprocessed parents.
            return cacheContainedAuthorities(groupName);
        }

        private Set<String> cacheContainedAuthorities(String groupName) {
            // Return the cached children if it is processed
            Set<String> children = this.finalGroupChildAssocs.get(groupName);
            if (children != null) {
                return children;
            }

            // Descend on unprocessed parents.
            children = TenantChainingUserRegistrySynchronizer.this.authorityService
                    .getContainedAuthorities(null, groupName, true);
            this.finalGroupChildAssocs.put(groupName, children);

            for (String child : children) {
                if (AuthorityType.getAuthorityType(child) != AuthorityType.USER) {
                    cacheContainedAuthorities(child);
                }
            }
            return children;
        }

        private synchronized void updateGroup(NodeDescription group, boolean existed) {
            PropertyMap groupProperties = group.getProperties();
            String groupName = (String) groupProperties.get(ContentModel.PROP_AUTHORITY_NAME);
            String groupDisplayName = (String) groupProperties.get(ContentModel.PROP_AUTHORITY_DISPLAY_NAME);
            if (groupDisplayName == null) {
                groupDisplayName = TenantChainingUserRegistrySynchronizer.this.authorityService
                        .getShortName(groupName);
            }

            // Divide the child associations into person and group
            // associations, dealing with case sensitivity
            Set<String> newChildPersons = newPersonSet();
            Set<String> newChildGroups = new TreeSet<String>();

            for (String child : group.getChildAssociations()) {
                if (AuthorityType.getAuthorityType(child) == AuthorityType.USER) {
                    newChildPersons.add(child);
                } else {
                    newChildGroups.add(child);
                }
            }

            // Account for differences if already existing
            if (existed) {
                // Update the display name now
                TenantChainingUserRegistrySynchronizer.this.authorityService.setAuthorityDisplayName(groupName,
                        groupDisplayName);

                // Work out the association differences
                for (String child : new TreeSet<String>(getContainedAuthorities(groupName))) {
                    if (AuthorityType.getAuthorityType(child) == AuthorityType.USER) {
                        if (!newChildPersons.remove(child)) {
                            recordParentAssociationDeletion(child, groupName);
                        }
                    } else {
                        if (!newChildGroups.remove(child)) {
                            recordParentAssociationDeletion(child, groupName);
                        }
                    }
                }
            }
            // Mark as created if new
            else {
                // Make sure each group to be created features in the
                // association deletion map (as these are handled in the
                // same phase)
                recordParentAssociationDeletion(groupName, null);
                this.groupsToCreate.put(groupName, groupDisplayName);
            }

            // Create new associations
            for (String child : newChildPersons) {
                // Make sure each person with association changes features
                // as a key in the deletion map
                recordParentAssociationDeletion(child, null);
                recordParentAssociationCreation(child, groupName);
            }
            for (String child : newChildGroups) {
                // Make sure each group with association changes features as
                // a key in the deletion map
                recordParentAssociationDeletion(child, null);
                recordParentAssociationCreation(child, groupName);
            }
        }

        private void recordParentAssociationDeletion(String child, String parent) {
            Map<String, Set<String>> parentAssocs;
            if (AuthorityType.getAuthorityType(child) == AuthorityType.USER) {
                parentAssocs = this.personParentAssocsToDelete;
            } else {
                // Reflect the change in the map of final group associations
                // (for cycle detection later)
                parentAssocs = this.groupParentAssocsToDelete;
                if (parent != null) {
                    Set<String> children = this.finalGroupChildAssocs.get(parent);
                    children.remove(child);
                }
            }
            Set<String> parents = parentAssocs.get(child);
            if (parents == null) {
                parents = new TreeSet<String>();
                parentAssocs.put(child, parents);
            }
            if (parent != null) {
                parents.add(parent);
            }
        }

        private void recordParentAssociationCreation(String child, String parent) {
            Map<String, Set<String>> parentAssocs = AuthorityType.getAuthorityType(child) == AuthorityType.USER
                    ? this.personParentAssocsToCreate
                    : this.groupParentAssocsToCreate;
            Set<String> parents = parentAssocs.get(child);
            if (parents == null) {
                parents = new TreeSet<String>();
                parentAssocs.put(child, parents);
            }
            if (parent != null) {
                parents.add(parent);
            }
        }

        private void validateGroupParentAssocsToCreate() {
            Iterator<Map.Entry<String, Set<String>>> i = this.groupParentAssocsToCreate.entrySet().iterator();
            while (i.hasNext()) {
                Map.Entry<String, Set<String>> entry = i.next();
                String group = entry.getKey();
                Set<String> parents = entry.getValue();
                Deque<String> visited = new LinkedList<String>();
                Iterator<String> j = parents.iterator();
                while (j.hasNext()) {
                    String parent = j.next();
                    visited.add(parent);
                    if (validateAuthorityChildren(visited, group)) {
                        // The association validated - commit it
                        Set<String> children = finalGroupChildAssocs.get(parent);
                        if (children == null) {
                            children = new TreeSet<String>();
                            finalGroupChildAssocs.put(parent, children);
                        }
                        children.add(group);
                    } else {
                        // The association did not validate - prune it out
                        if (logger.isWarnEnabled()) {
                            TenantChainingUserRegistrySynchronizer.logger.warn("Not adding group '"
                                    + TenantChainingUserRegistrySynchronizer.this.authorityService
                                            .getShortName(group)
                                    + "' to group '"
                                    + TenantChainingUserRegistrySynchronizer.this.authorityService
                                            .getShortName(parent)
                                    + "' as this creates a cyclic relationship");
                        }
                        j.remove();
                    }
                    visited.removeLast();
                }
                if (parents.isEmpty()) {
                    i.remove();
                }
            }

            // Sort the group associations in parent-first order (root
            // groups first) to minimize reindexing overhead
            Map<String, Set<String>> sortedGroupAssociations = new LinkedHashMap<String, Set<String>>(
                    this.groupParentAssocsToCreate.size() * 2);
            Deque<String> visited = new LinkedList<String>();
            for (String authority : this.groupParentAssocsToCreate.keySet()) {
                visitGroupParentAssocs(visited, authority, this.groupParentAssocsToCreate,
                        sortedGroupAssociations);
            }

            this.groupParentAssocsToCreate = sortedGroupAssociations;
        }

        private boolean validateAuthorityChildren(Deque<String> visited, String authority) {
            if (AuthorityType.getAuthorityType(authority) == AuthorityType.USER) {
                return true;
            }
            if (visited.contains(authority)) {
                return false;
            }
            visited.add(authority);
            try {
                Set<String> children = this.finalGroupChildAssocs.get(authority);
                if (children != null) {
                    for (String child : children) {
                        if (!validateAuthorityChildren(visited, child)) {
                            return false;
                        }
                    }
                }
                return true;
            } finally {
                visited.removeLast();
            }
        }

        /**
         * Visits the given authority by recursively visiting its parents in
         * associationsOld and then adding the authority to associationsNew.
         * Used to sort associationsOld into 'parent-first' order to
         * minimize reindexing overhead.
         * 
         * @param visited
         *            The ancestors that form the path to the authority to
         *            visit. Allows detection of cyclic child associations.
         * @param authority
         *            the authority to visit
         * @param associationsOld
         *            the association map to sort
         * @param associationsNew
         *            the association map to add to in parent-first order
         */
        private boolean visitGroupParentAssocs(Deque<String> visited, String authority,
                Map<String, Set<String>> associationsOld, Map<String, Set<String>> associationsNew) {
            if (visited.contains(authority)) {
                // Prevent cyclic paths (Shouldn't happen as we've already
                // validated)
                return false;
            }
            visited.add(authority);
            try {
                if (!associationsNew.containsKey(authority)) {
                    Set<String> oldParents = associationsOld.get(authority);
                    if (oldParents != null) {
                        Set<String> newParents = new TreeSet<String>();

                        for (String parent : oldParents) {
                            if (visitGroupParentAssocs(visited, parent, associationsOld, associationsNew)) {
                                newParents.add(parent);
                            }
                        }
                        associationsNew.put(authority, newParents);
                    }
                }
                return true;
            } finally {
                visited.removeLast();
            }
        }

        private Set<String> newPersonSet() {
            return TenantChainingUserRegistrySynchronizer.this.personService.getUserNamesAreCaseSensitive()
                    ? new TreeSet<String>()
                    : new TreeSet<String>(String.CASE_INSENSITIVE_ORDER);
        }

        private Map<String, Set<String>> newPersonMap() {
            return TenantChainingUserRegistrySynchronizer.this.personService.getUserNamesAreCaseSensitive()
                    ? new TreeMap<String, Set<String>>()
                    : new TreeMap<String, Set<String>>(String.CASE_INSENSITIVE_ORDER);
        }

        private void logRetainParentAssociations(Map<String, Set<String>> parentAssocs, Set<String> toRetain) {
            if (toRetain.isEmpty()) {
                parentAssocs.clear();
                return;
            }
            Iterator<Map.Entry<String, Set<String>>> i = parentAssocs.entrySet().iterator();
            StringBuilder groupList = null;
            while (i.hasNext()) {
                Map.Entry<String, Set<String>> entry = i.next();
                String child = entry.getKey();
                if (!toRetain.contains(child)) {
                    if (TenantChainingUserRegistrySynchronizer.logger.isDebugEnabled()) {
                        if (groupList == null) {
                            groupList = new StringBuilder(1024);
                        } else {
                            groupList.setLength(0);
                        }
                        for (String parent : entry.getValue()) {
                            if (groupList.length() > 0) {
                                groupList.append(", ");
                            }
                            groupList.append('\'')
                                    .append(TenantChainingUserRegistrySynchronizer.this.authorityService
                                            .getShortName(parent))
                                    .append('\'');

                        }
                        TenantChainingUserRegistrySynchronizer.logger.debug("Ignoring non-existent member '"
                                + TenantChainingUserRegistrySynchronizer.this.authorityService
                                        .getShortName(child)
                                + "' in groups {" + groupList.toString() + "}. RunAs user:"
                                + AuthenticationUtil.getRunAsUser());
                    }
                    i.remove();
                }
            }
        }

        public void processGroups(UserRegistry userRegistry, boolean isFullSync, boolean splitTxns) {
            // If we got back some groups, we have to cross reference them
            // with the set of known authorities
            if (isFullSync || !this.groupParentAssocsToDelete.isEmpty()
                    || !this.groupParentAssocsToDelete.isEmpty()) {
                processGroupSynchInTenantMode(userRegistry, isFullSync, splitTxns);
            }
        }

        private void processGroupSynchInTenantMode(UserRegistry userRegistry, boolean isFullSync,
                boolean splitTxns) {
            final Set<String> allZonePersons = newPersonSet();
            final Set<String> allZoneGroups = new TreeSet<String>();
            final String tenantId = SEIPTenantIntegration.getTenantId();
            // Add in current set of known authorities
            TenantChainingUserRegistrySynchronizer.this.transactionService.getRetryingTransactionHelper()
                    .doInTransaction(new RetryingTransactionCallback<Void>() {
                        public Void execute() throws Throwable {
                            allZonePersons.addAll(TenantChainingUserRegistrySynchronizer.this.authorityService
                                    .getAllAuthoritiesInZone(zoneId, AuthorityType.USER));
                            allZoneGroups.addAll(TenantChainingUserRegistrySynchronizer.this.authorityService
                                    .getAllAuthoritiesInZone(zoneId, AuthorityType.GROUP));
                            return null;
                        }
                    }, true, splitTxns);

            allZoneGroups.addAll(this.groupsToCreate.keySet());

            // Prune our set of authorities according to deletions
            if (isFullSync) {
                final Set<String> personDeletionCandidates = newPersonSet();
                personDeletionCandidates.addAll(allZonePersons);

                final Set<String> groupDeletionCandidates = new TreeSet<String>();
                groupDeletionCandidates.addAll(allZoneGroups);

                this.deletionCandidates = new TreeSet<String>();

                for (String person : userRegistry.getPersonNames()) {
                    personDeletionCandidates.remove(person);
                }

                for (String group : userRegistry.getGroupNames()) {
                    groupDeletionCandidates.remove(group);
                }

                this.deletionCandidates = new TreeSet<String>();
                this.deletionCandidates.addAll(personDeletionCandidates);
                this.deletionCandidates.addAll(groupDeletionCandidates);
                if (allowDeletions) {
                    allZonePersons.removeAll(personDeletionCandidates);
                    allZoneGroups.removeAll(groupDeletionCandidates);
                } else {
                    if (!personDeletionCandidates.isEmpty()) {
                        TenantChainingUserRegistrySynchronizer.logger.warn(
                                "The following missing users are not being deleted as allowDeletions == false");
                        for (String person : personDeletionCandidates) {
                            TenantChainingUserRegistrySynchronizer.logger.warn("    " + person);
                        }
                    }
                    if (!groupDeletionCandidates.isEmpty()) {
                        TenantChainingUserRegistrySynchronizer.logger.warn(
                                "The following missing groups are not being deleted as allowDeletions == false");
                        for (String group : groupDeletionCandidates) {
                            TenantChainingUserRegistrySynchronizer.logger.warn("    " + group);
                        }
                    }

                    // Complete association deletion information by
                    // scanning deleted groups
                    BatchProcessor<String> groupScanner = new BatchProcessor<String>(
                            zone + " Missing Authority Scanning",
                            TenantChainingUserRegistrySynchronizer.this.transactionService
                                    .getRetryingTransactionHelper(),
                            this.deletionCandidates, TenantChainingUserRegistrySynchronizer.this.workerThreads,
                            20, TenantChainingUserRegistrySynchronizer.this.applicationEventPublisher,
                            TenantChainingUserRegistrySynchronizer.logger,
                            TenantChainingUserRegistrySynchronizer.this.loggingInterval);
                    groupScanner.process(new BaseBatchProcessWorker<String>() {

                        @Override
                        public String getIdentifier(String entry) {
                            return entry;
                        }

                        @Override
                        public void process(final String authority) throws Throwable {

                            AuthenticationUtil.runAs(new RunAsWork<Void>() {

                                @Override
                                public Void doWork() throws Exception {
                                    proceesInTenantMode(zoneId, authority);
                                    return null;
                                }
                            }, SEIPTenantIntegration.getSystemUserByTenantId(tenantId));

                        }

                        private void proceesInTenantMode(final String zoneId, String authority) {
                            // Disassociate it from this zone, allowing
                            // it to be reclaimed by something further
                            // down the chain
                            TenantChainingUserRegistrySynchronizer.this.authorityService
                                    .removeAuthorityFromZones(authority, Collections.singleton(zoneId));

                            // For groups, remove all members
                            if (AuthorityType.getAuthorityType(authority) != AuthorityType.USER) {
                                String groupShortName = TenantChainingUserRegistrySynchronizer.this.authorityService
                                        .getShortName(authority);
                                String groupDisplayName = TenantChainingUserRegistrySynchronizer.this.authorityService
                                        .getAuthorityDisplayName(authority);
                                NodeDescription dummy = new NodeDescription(groupShortName + " (Deleted)");
                                PropertyMap dummyProperties = dummy.getProperties();
                                dummyProperties.put(ContentModel.PROP_AUTHORITY_NAME, authority);
                                if (groupDisplayName != null) {
                                    dummyProperties.put(ContentModel.PROP_AUTHORITY_DISPLAY_NAME,
                                            groupDisplayName);
                                }
                                updateGroup(dummy, true);
                            }
                        }
                    }, splitTxns);

                }
            }

            // Prune the group associations now that we have complete
            // information
            this.groupParentAssocsToCreate.keySet().retainAll(allZoneGroups);
            logRetainParentAssociations(this.groupParentAssocsToCreate, allZoneGroups);
            this.finalGroupChildAssocs.keySet().retainAll(allZoneGroups);

            // Pruning person associations will have to wait until we
            // have passed over all persons and built up
            // this set
            this.allZonePersons = allZonePersons;

            if (!this.groupParentAssocsToDelete.isEmpty()) {
                // Create/update the groups and delete parent
                // associations to be deleted
                BatchProcessor<Map.Entry<String, Set<String>>> groupCreator = new BatchProcessor<Map.Entry<String, Set<String>>>(
                        zone + " Group Creation and Association Deletion",
                        TenantChainingUserRegistrySynchronizer.this.transactionService
                                .getRetryingTransactionHelper(),
                        this.groupParentAssocsToDelete.entrySet(),
                        TenantChainingUserRegistrySynchronizer.this.workerThreads, 20,
                        TenantChainingUserRegistrySynchronizer.this.applicationEventPublisher,
                        TenantChainingUserRegistrySynchronizer.logger,
                        TenantChainingUserRegistrySynchronizer.this.loggingInterval);
                groupCreator.process(new BaseBatchProcessWorker<Map.Entry<String, Set<String>>>() {
                    public String getIdentifier(Map.Entry<String, Set<String>> entry) {
                        return entry.getKey() + " " + entry.getValue();
                    }

                    public void process(final Map.Entry<String, Set<String>> entry) throws Throwable {

                        AuthenticationUtil.runAs(new RunAsWork<Void>() {

                            @Override
                            public Void doWork() throws Exception {
                                processInternal(zoneSet, entry.getKey());
                                return null;
                            }
                        }, SEIPTenantIntegration.getSystemUserByTenantId(tenantId));

                    }

                    private void processInternal(final Set<String> zoneSet, String child) {
                        String groupDisplayName = Analyzer.this.groupsToCreate.get(child);
                        if (groupDisplayName != null) {
                            String groupShortName = TenantChainingUserRegistrySynchronizer.this.authorityService
                                    .getShortName(child);
                            if (TenantChainingUserRegistrySynchronizer.logger.isDebugEnabled()) {
                                TenantChainingUserRegistrySynchronizer.logger
                                        .debug("Creating group '" + groupShortName + "'");
                            }
                            // create the group
                            TenantChainingUserRegistrySynchronizer.this.authorityService.createAuthority(
                                    AuthorityType.getAuthorityType(child), groupShortName, groupDisplayName,
                                    zoneSet);
                        } else {
                            // Maintain association deletions now. The
                            // creations will have to be done later once
                            // we have performed all the deletions in
                            // order to avoid creating cycles
                            maintainAssociationDeletions(child);
                        }
                    }
                }, splitTxns);
            }
        }

        public void finalizeAssociations(UserRegistry userRegistry, boolean splitTxns) {
            // First validate the group associations to be created for
            // potential cycles. Remove any offending association
            validateGroupParentAssocsToCreate();

            // Now go ahead and create the group associations
            if (!this.groupParentAssocsToCreate.isEmpty()) {
                BatchProcessor<Map.Entry<String, Set<String>>> groupCreator = new BatchProcessor<Map.Entry<String, Set<String>>>(
                        zone + " Group Association Creation",
                        TenantChainingUserRegistrySynchronizer.this.transactionService
                                .getRetryingTransactionHelper(),
                        this.groupParentAssocsToCreate.entrySet(),
                        TenantChainingUserRegistrySynchronizer.this.workerThreads, 20,
                        TenantChainingUserRegistrySynchronizer.this.applicationEventPublisher,
                        TenantChainingUserRegistrySynchronizer.logger,
                        TenantChainingUserRegistrySynchronizer.this.loggingInterval);
                groupCreator.process(new BaseBatchProcessWorker<Map.Entry<String, Set<String>>>() {
                    public String getIdentifier(Map.Entry<String, Set<String>> entry) {
                        return entry.getKey() + " " + entry.getValue();
                    }

                    public void process(Map.Entry<String, Set<String>> entry) throws Throwable {

                        final String user = entry.getKey();
                        AuthenticationUtil.runAs(new RunAsWork<Void>() {

                            @Override
                            public Void doWork() throws Exception {
                                maintainAssociationCreations(user);
                                return null;
                            }
                        }, SEIPTenantIntegration.getSystemUser(user));
                    }
                }, splitTxns);
            }

            // Remove all the associations we have already dealt with
            this.personParentAssocsToDelete.keySet().removeAll(this.personsProcessed);

            // Filter out associations to authorities that simply can't
            // exist (and log if debugging is enabled)
            logRetainParentAssociations(this.personParentAssocsToCreate, this.allZonePersons);

            // Update associations to persons not updated themselves
            if (!this.personParentAssocsToDelete.isEmpty()) {
                BatchProcessor<Map.Entry<String, Set<String>>> groupCreator = new BatchProcessor<Map.Entry<String, Set<String>>>(
                        zone + " Person Association",
                        TenantChainingUserRegistrySynchronizer.this.transactionService
                                .getRetryingTransactionHelper(),
                        this.personParentAssocsToDelete.entrySet(),
                        TenantChainingUserRegistrySynchronizer.this.workerThreads, 20,
                        TenantChainingUserRegistrySynchronizer.this.applicationEventPublisher,
                        TenantChainingUserRegistrySynchronizer.logger,
                        TenantChainingUserRegistrySynchronizer.this.loggingInterval);
                groupCreator.process(new BaseBatchProcessWorker<Map.Entry<String, Set<String>>>() {
                    public String getIdentifier(Map.Entry<String, Set<String>> entry) {
                        return entry.getKey() + " " + entry.getValue();
                    }

                    public void process(final Map.Entry<String, Set<String>> entry) throws Throwable {
                        final String user = entry.getKey();
                        AuthenticationUtil.runAs(new RunAsWork<Void>() {

                            @Override
                            public Void doWork() throws Exception {
                                maintainAssociationDeletions(user);
                                maintainAssociationCreations(user);
                                return null;
                            }
                        }, SEIPTenantIntegration.getSystemUser(user));

                    }
                }, splitTxns);
            }
        }

        private void maintainAssociationDeletions(String authorityName) {
            boolean isPerson = AuthorityType.getAuthorityType(authorityName) == AuthorityType.USER;
            Set<String> parentsToDelete = isPerson ? this.personParentAssocsToDelete.get(authorityName)
                    : this.groupParentAssocsToDelete.get(authorityName);
            if (parentsToDelete != null && !parentsToDelete.isEmpty()) {
                for (String parent : parentsToDelete) {
                    if (TenantChainingUserRegistrySynchronizer.logger.isDebugEnabled()) {
                        TenantChainingUserRegistrySynchronizer.logger.debug("Removing '"
                                + TenantChainingUserRegistrySynchronizer.this.authorityService
                                        .getShortName(authorityName)
                                + "' from group '"
                                + TenantChainingUserRegistrySynchronizer.this.authorityService
                                        .getShortName(parent)
                                + "'");
                    }
                    TenantChainingUserRegistrySynchronizer.this.authorityService.removeAuthority(parent,
                            authorityName);
                }
            }

        }

        private void maintainAssociationCreations(String authorityName) {
            boolean isPerson = AuthorityType.getAuthorityType(authorityName) == AuthorityType.USER;
            Set<String> parents = isPerson ? this.personParentAssocsToCreate.get(authorityName)
                    : this.groupParentAssocsToCreate.get(authorityName);
            if (parents != null && !parents.isEmpty()) {
                if (TenantChainingUserRegistrySynchronizer.logger.isDebugEnabled()) {
                    for (String groupName : parents) {
                        TenantChainingUserRegistrySynchronizer.logger.debug("Adding '"
                                + TenantChainingUserRegistrySynchronizer.this.authorityService
                                        .getShortName(authorityName)
                                + "' to group '" + TenantChainingUserRegistrySynchronizer.this.authorityService
                                        .getShortName(groupName)
                                + "'");
                    }
                }
                try {
                    TenantChainingUserRegistrySynchronizer.this.authorityService.addAuthority(parents,
                            authorityName);
                } catch (UnknownAuthorityException e) {
                    // Let's force a transaction retry if a parent doesn't
                    // exist. It may be because we are
                    // waiting for another worker thread to create it
                    throw new ConcurrencyFailureException("Forcing batch retry for unknown authority", e);
                } catch (InvalidNodeRefException e) {
                    // Another thread may have written the node, but it is
                    // not visible to this transaction
                    // See: ALF-5471: 'authorityMigration' patch can report
                    // 'Node does not exist'
                    throw new ConcurrencyFailureException("Forcing batch retry for invalid node", e);
                }
            }
            // Remember that this person's associations have been maintained
            if (isPerson) {
                synchronized (this) {
                    this.personsProcessed.add(authorityName);
                }
            }
        }
    }

    final Analyzer groupAnalyzer = new Analyzer(lastModifiedMillis);
    int groupProcessedCount = groupProcessor.process(groupAnalyzer, splitTxns);

    groupAnalyzer.processGroups(userRegistry, isFullSync, splitTxns);

    // Process persons and their parent associations

    lastModifiedMillis = forceUpdate ? -1
            : getMostRecentUpdateTime(TenantChainingUserRegistrySynchronizer.PERSON_LAST_MODIFIED_ATTRIBUTE,
                    zoneId, splitTxns);
    lastModified = lastModifiedMillis == -1 ? null : new Date(lastModifiedMillis);
    if (TenantChainingUserRegistrySynchronizer.logger.isInfoEnabled()) {
        if (lastModified == null) {
            TenantChainingUserRegistrySynchronizer.logger
                    .info("Retrieving all users from user registry '" + zone + "'");
        } else {
            TenantChainingUserRegistrySynchronizer.logger.info(
                    "Retrieving users changed since " + DateFormat.getDateTimeInstance().format(lastModified)
                            + " from user registry '" + zone + "'");
        }
    }
    final BatchProcessor<NodeDescription> personProcessor = new BatchProcessor<NodeDescription>(
            zone + " User Creation and Association", this.transactionService.getRetryingTransactionHelper(),
            userRegistry.getPersons(lastModified), this.workerThreads, 10, this.applicationEventPublisher,
            TenantChainingUserRegistrySynchronizer.logger, this.loggingInterval);
    class PersonWorker extends BaseBatchProcessWorker<NodeDescription> {
        private long latestTime;

        public PersonWorker(final long latestTime) {
            this.latestTime = latestTime;
        }

        public long getLatestTime() {
            return this.latestTime;
        }

        public String getIdentifier(NodeDescription entry) {
            return entry.getSourceId();
        }

        public void process(final NodeDescription person) throws Throwable {
            // Make a mutable copy of the person properties, since they get
            // written back to by person service
            HashMap<QName, Serializable> personProperties = new HashMap<QName, Serializable>(
                    person.getProperties());
            String personName = (String) personProperties.get(ContentModel.PROP_USERNAME);
            String ou = (String) personProperties.get(ContentModel.PROP_ORGANIZATION);
            if (SEIPTenantIntegration.isValidTenant(ou) && !personName.endsWith(ou)) {
                personName += ("@" + ou);
                person.getProperties().put(ContentModel.PROP_USERNAME, personName);
            }
            final String personFullName = personName;
            logger.debug("Check user: " + personFullName);
            String tenantDomain = SEIPTenantIntegration.getTenantId(personFullName);
            if (tenantDomain != null && !tenantDomain.isEmpty()) {
                if (!isTenantEnabled(tenantDomain)) {
                    logger.debug("Tenant is missing/disabled for user: " + personFullName);
                    return;
                }
                logger.debug("Process user: " + personFullName);
                AuthenticationUtil.runAs(new RunAsWork<Void>() {
                    @Override
                    public Void doWork() throws Exception {
                        processInTenantMode(personFullName, person);
                        return null;
                    }
                }, SEIPTenantIntegration.getSystemUserByTenantId(tenantDomain));
            } else {
                logger.debug("Process user: " + personFullName);
                processInTenantMode(personFullName, person);
            }
        }

        private void processInTenantMode(String personName, NodeDescription person) {
            HashMap<QName, Serializable> personProperties = person.getProperties();
            // Make a mutable copy of the person properties, since they get
            // written back to by person service

            Set<String> zones = TenantChainingUserRegistrySynchronizer.this.authorityService
                    .getAuthorityZones(personName);
            if (zones == null) {
                // The person did not exist at all
                if (TenantChainingUserRegistrySynchronizer.logger.isDebugEnabled()) {
                    TenantChainingUserRegistrySynchronizer.logger.debug("Creating user '" + personName + "'");
                }

                TenantChainingUserRegistrySynchronizer.this.personService.createPerson(personProperties,
                        zoneSet);
            } else if (zones.contains(zoneId)) {
                // The person already existed in this zone: update the
                // person
                if (TenantChainingUserRegistrySynchronizer.logger.isDebugEnabled()) {
                    TenantChainingUserRegistrySynchronizer.logger.debug("Updating user '" + personName + "'");
                }

                TenantChainingUserRegistrySynchronizer.this.personService.setPersonProperties(personName,
                        personProperties, false);
            } else {
                // Check whether the user is in any of the authentication
                // chain zones
                Set<String> intersection = new TreeSet<String>(zones);
                intersection.retainAll(allZoneIds);
                // Check whether the user is in any of the higher priority
                // authentication chain zones
                Set<String> visited = new TreeSet<String>(intersection);
                visited.retainAll(visitedZoneIds);
                if (visited.size() > 0) {
                    // A person that exists in a different zone with higher
                    // precedence - ignore
                    return;
                }

                else if (!allowDeletions || intersection.isEmpty()) {
                    // The person exists, but in a different zone. Either
                    // deletions are disallowed or the zone is
                    // not in the authentication chain. May be due to
                    // upgrade or zone changes. Let's re-zone them
                    if (TenantChainingUserRegistrySynchronizer.logger.isWarnEnabled()) {
                        TenantChainingUserRegistrySynchronizer.logger.warn("Updating user '" + personName
                                + "'. This user will in future be assumed to originate from user registry '"
                                + zone + "'.");
                    }
                    updateAuthorityZones(personName, zones, zoneSet);
                    TenantChainingUserRegistrySynchronizer.this.personService.setPersonProperties(personName,
                            personProperties, false);
                } else {
                    // The person existed, but in a zone with lower
                    // precedence
                    if (TenantChainingUserRegistrySynchronizer.logger.isWarnEnabled()) {
                        TenantChainingUserRegistrySynchronizer.logger.warn("Recreating occluded user '"
                                + personName
                                + "'. This user was previously created through synchronization with a lower priority user registry.");
                    }
                    TenantChainingUserRegistrySynchronizer.this.personService.deletePerson(personName);
                    TenantChainingUserRegistrySynchronizer.this.personService.createPerson(personProperties,
                            zoneSet);
                }
            }

            // Maintain association deletions and creations in one shot
            // (safe to do this with persons as we can't
            // create cycles)
            groupAnalyzer.maintainAssociationDeletions(personName);
            groupAnalyzer.maintainAssociationCreations(personName);

            synchronized (this) {
                // Maintain the last modified date
                Date personLastModified = person.getLastModified();
                if (personLastModified != null) {
                    this.latestTime = Math.max(this.latestTime, personLastModified.getTime());
                }
            }
        }
    }

    PersonWorker persons = new PersonWorker(lastModifiedMillis);
    int personProcessedCount = personProcessor.process(persons, splitTxns);

    // Process those associations to persons who themselves have not been
    // updated
    groupAnalyzer.finalizeAssociations(userRegistry, splitTxns);

    // Only now that the whole tree has been processed is it safe to persist
    // the last modified dates
    long latestTime = groupAnalyzer.getLatestTime();
    if (latestTime != -1) {
        setMostRecentUpdateTime(TenantChainingUserRegistrySynchronizer.GROUP_LAST_MODIFIED_ATTRIBUTE, zoneId,
                latestTime, splitTxns);
    }
    latestTime = persons.getLatestTime();
    if (latestTime != -1) {
        setMostRecentUpdateTime(TenantChainingUserRegistrySynchronizer.PERSON_LAST_MODIFIED_ATTRIBUTE, zoneId,
                latestTime, splitTxns);
    }

    // Delete authorities if we have complete information for the zone
    Set<String> deletionCandidates = groupAnalyzer.getDeletionCandidates();
    if (isFullSync && allowDeletions && !deletionCandidates.isEmpty()) {
        BatchProcessor<String> authorityDeletionProcessor = new BatchProcessor<String>(
                zone + " Authority Deletion", this.transactionService.getRetryingTransactionHelper(),
                deletionCandidates, this.workerThreads, 10, this.applicationEventPublisher,
                TenantChainingUserRegistrySynchronizer.logger, this.loggingInterval);
        class AuthorityDeleter extends BaseBatchProcessWorker<String> {
            private int personProcessedCount;
            private int groupProcessedCount;

            public int getPersonProcessedCount() {
                return this.personProcessedCount;
            }

            public int getGroupProcessedCount() {
                return this.groupProcessedCount;
            }

            public String getIdentifier(String entry) {
                return entry;
            }

            public void process(String authority) throws Throwable {
                if (AuthorityType.getAuthorityType(authority) == AuthorityType.USER) {
                    if (TenantChainingUserRegistrySynchronizer.logger.isDebugEnabled()) {
                        TenantChainingUserRegistrySynchronizer.logger
                                .debug("Deleting user '" + authority + "'");
                    }
                    TenantChainingUserRegistrySynchronizer.this.personService.deletePerson(authority);
                    synchronized (this) {
                        this.personProcessedCount++;
                    }
                } else {
                    if (TenantChainingUserRegistrySynchronizer.logger.isDebugEnabled()) {
                        TenantChainingUserRegistrySynchronizer.logger.debug("Deleting group '"
                                + TenantChainingUserRegistrySynchronizer.this.authorityService
                                        .getShortName(authority)
                                + "'");
                    }
                    TenantChainingUserRegistrySynchronizer.this.authorityService.deleteAuthority(authority);
                    synchronized (this) {
                        this.groupProcessedCount++;
                    }
                }
            }
        }
        AuthorityDeleter authorityDeleter = new AuthorityDeleter();
        authorityDeletionProcessor.process(authorityDeleter, splitTxns);
        groupProcessedCount += authorityDeleter.getGroupProcessedCount();
        personProcessedCount += authorityDeleter.getPersonProcessedCount();
    }

    // Remember we have visited this zone
    visitedZoneIds.add(zoneId);

    if (TenantChainingUserRegistrySynchronizer.logger.isInfoEnabled()) {
        TenantChainingUserRegistrySynchronizer.logger
                .info("Finished synchronizing users and groups with user registry '" + zone + "'");
        TenantChainingUserRegistrySynchronizer.logger
                .info(personProcessedCount + " user(s) and " + groupProcessedCount + " group(s) processed");
    }
}

From source file:op.care.bhp.PnlBHP.java

private CollapsiblePane createCP4(final BHP bhp) {
    final CollapsiblePane bhpPane = new CollapsiblePane();
    bhpPane.setCollapseOnTitleClick(false);

    ActionListener applyActionListener = new ActionListener() {
        @Override//  w ww. j  a  v  a 2 s  . c om
        public void actionPerformed(ActionEvent actionEvent) {
            if (bhp.getState() != BHPTools.STATE_OPEN) {
                return;
            }
            if (bhp.getPrescription().isClosed()) {
                return;
            }

            if (BHPTools.isChangeable(bhp)) {
                outcomeText = null;
                if (bhp.getNeedsText()) {
                    new DlgYesNo(SYSConst.icon48comment, new Closure() {
                        @Override
                        public void execute(Object o) {
                            if (SYSTools.catchNull(o).isEmpty()) {
                                outcomeText = null;
                            } else {
                                outcomeText = o.toString();
                            }
                        }
                    }, "nursingrecords.bhp.describe.outcome", null, null);

                }

                if (bhp.getNeedsText() && outcomeText == null) {
                    OPDE.getDisplayManager().addSubMessage(
                            new DisplayMessage("nursingrecords.bhp.notext.nooutcome", DisplayMessage.WARNING));
                    return;
                }

                if (bhp.getPrescription().isWeightControlled()) {
                    new DlgYesNo(SYSConst.icon48scales, new Closure() {
                        @Override
                        public void execute(Object o) {
                            if (SYSTools.catchNull(o).isEmpty()) {
                                weight = null;
                            } else {
                                weight = (BigDecimal) o;
                            }
                        }
                    }, "nursingrecords.bhp.weight", null, new Validator<BigDecimal>() {
                        @Override
                        public boolean isValid(String value) {
                            BigDecimal bd = parse(value);
                            return bd != null && bd.compareTo(BigDecimal.ZERO) > 0;

                        }

                        @Override
                        public BigDecimal parse(String text) {
                            return SYSTools.parseDecimal(text);
                        }
                    });

                }

                if (bhp.getPrescription().isWeightControlled() && weight == null) {
                    OPDE.getDisplayManager().addSubMessage(new DisplayMessage(
                            "nursingrecords.bhp.noweight.nosuccess", DisplayMessage.WARNING));
                    return;
                }

                EntityManager em = OPDE.createEM();
                try {
                    em.getTransaction().begin();

                    em.lock(em.merge(resident), LockModeType.OPTIMISTIC);
                    BHP myBHP = em.merge(bhp);
                    em.lock(myBHP, LockModeType.OPTIMISTIC);

                    if (myBHP.isOnDemand()) {
                        em.lock(myBHP.getPrescriptionSchedule(), LockModeType.OPTIMISTIC_FORCE_INCREMENT);
                        em.lock(myBHP.getPrescription(), LockModeType.OPTIMISTIC_FORCE_INCREMENT);
                    } else {
                        em.lock(myBHP.getPrescriptionSchedule(), LockModeType.OPTIMISTIC);
                        em.lock(myBHP.getPrescription(), LockModeType.OPTIMISTIC);
                    }

                    myBHP.setState(BHPTools.STATE_DONE);
                    myBHP.setUser(em.merge(OPDE.getLogin().getUser()));
                    myBHP.setIst(new Date());
                    myBHP.setiZeit(SYSCalendar.whatTimeIDIs(new Date()));
                    myBHP.setMDate(new Date());
                    myBHP.setText(outcomeText);

                    Prescription involvedPresciption = null;
                    if (myBHP.shouldBeCalculated()) {
                        MedInventory inventory = TradeFormTools.getInventory4TradeForm(resident,
                                myBHP.getTradeForm());
                        MedInventoryTools.withdraw(em, em.merge(inventory), myBHP.getDose(), weight, myBHP);
                        // Was the prescription closed during this withdraw ?
                        involvedPresciption = em.find(Prescription.class, myBHP.getPrescription().getID());
                    }

                    BHP outcomeBHP = null;
                    // add outcome check BHP if necessary
                    if (!myBHP.isOutcomeText()
                            && myBHP.getPrescriptionSchedule().getCheckAfterHours() != null) {
                        outcomeBHP = em.merge(new BHP(myBHP));
                        mapShift2BHP.get(BHPTools.SHIFT_ON_DEMAND).add(outcomeBHP);
                    }

                    em.getTransaction().commit();

                    if (myBHP.shouldBeCalculated() && involvedPresciption.isClosed()) { // &&
                        reload();
                    } else if (outcomeBHP != null) {
                        reload();
                    } else {
                        mapBHP2Pane.put(myBHP, createCP4(myBHP));
                        int position = mapShift2BHP.get(myBHP.getShift()).indexOf(bhp);
                        mapShift2BHP.get(myBHP.getShift()).remove(position);
                        mapShift2BHP.get(myBHP.getShift()).add(position, myBHP);
                        if (myBHP.isOnDemand()) {
                            // This whole thing here is only to handle the BPHs on Demand
                            // Fix the other BHPs on demand. If not, you will get locking exceptions,
                            // we FORCED INCREMENTED LOCKS on the Schedule and the Prescription.
                            ArrayList<BHP> changeList = new ArrayList<BHP>();
                            for (BHP bhp : mapShift2BHP.get(BHPTools.SHIFT_ON_DEMAND)) {
                                if (bhp.getPrescription().getID() == myBHP.getPrescription().getID()
                                        && bhp.getBHPid() != myBHP.getBHPid()) {
                                    bhp.setPrescription(myBHP.getPrescription());
                                    bhp.setPrescriptionSchedule(myBHP.getPrescriptionSchedule());
                                    changeList.add(bhp);
                                }
                            }
                            for (BHP bhp : changeList) {
                                mapBHP2Pane.put(bhp, createCP4(myBHP));
                                position = mapShift2BHP.get(bhp.getShift()).indexOf(bhp);
                                mapShift2BHP.get(myBHP.getShift()).remove(position);
                                mapShift2BHP.get(myBHP.getShift()).add(position, bhp);
                            }

                            Collections.sort(mapShift2BHP.get(myBHP.getShift()),
                                    BHPTools.getOnDemandComparator());
                        } else {
                            Collections.sort(mapShift2BHP.get(myBHP.getShift()));
                        }

                        mapShift2Pane.put(myBHP.getShift(), createCP4(myBHP.getShift()));
                        buildPanel(false);
                    }
                } catch (OptimisticLockException ole) {
                    OPDE.warn(ole);
                    if (em.getTransaction().isActive()) {
                        em.getTransaction().rollback();
                    }
                    if (ole.getMessage().indexOf("Class> entity.info.Resident") > -1) {
                        OPDE.getMainframe().emptyFrame();
                        OPDE.getMainframe().afterLogin();
                    }
                    OPDE.getDisplayManager().addSubMessage(DisplayManager.getLockMessage());
                } catch (RollbackException ole) {
                    if (em.getTransaction().isActive()) {
                        em.getTransaction().rollback();
                    }
                    if (ole.getMessage().indexOf("Class> entity.info.Resident") > -1) {
                        OPDE.getMainframe().emptyFrame();
                        OPDE.getMainframe().afterLogin();
                    }
                    OPDE.getDisplayManager().addSubMessage(DisplayManager.getLockMessage());
                } catch (Exception e) {
                    if (em.getTransaction().isActive()) {
                        em.getTransaction().rollback();
                    }
                    OPDE.fatal(e);
                } finally {
                    em.close();
                }

            } else {
                OPDE.getDisplayManager()
                        .addSubMessage(new DisplayMessage(SYSTools.xx("nursingrecords.bhp.notchangeable")));
            }
        }
    };

    //        JPanel titlePanelleft = new JPanel();
    //        titlePanelleft.setLayout(new BoxLayout(titlePanelleft, BoxLayout.LINE_AXIS));

    MedStock stock = mapPrescription2Stock.get(bhp.getPrescription());
    if (bhp.hasMed() && stock == null) {
        stock = MedStockTools
                .getStockInUse(TradeFormTools.getInventory4TradeForm(resident, bhp.getTradeForm()));
        mapPrescription2Stock.put(bhp.getPrescription(), stock);
    }

    String title;

    if (bhp.isOutcomeText()) {
        title = "<html><font size=+1>"
                + SYSConst.html_italic(SYSTools
                        .left("&ldquo;" + PrescriptionTools.getShortDescriptionAsCompactText(
                                bhp.getPrescriptionSchedule().getPrescription()), MAX_TEXT_LENGTH)
                        + BHPTools.getScheduleText(bhp.getOutcome4(), "&rdquo;, ", ""))
                + " [" + bhp.getPrescriptionSchedule().getCheckAfterHours() + " "
                + SYSTools.xx("misc.msg.Hour(s)") + "] " + BHPTools.getScheduleText(bhp, ", ", "")
                + (bhp.getPrescription().isWeightControlled()
                        ? " " + SYSConst.html_16x16_scales_internal + (bhp.isOpen() ? ""
                                : (bhp.getStockTransaction().isEmpty() ? " "
                                        : NumberFormat.getNumberInstance()
                                                .format(bhp.getStockTransaction().get(0).getWeight()) + "g "))
                        : "")
                + (bhp.getUser() != null ? ", <i>" + SYSTools.anonymizeUser(bhp.getUser().getUID()) + "</i>"
                        : "")
                +

                "</font></html>";
    } else {
        title = "<html><font size=+1>"
                + SYSTools.left(PrescriptionTools.getShortDescriptionAsCompactText(
                        bhp.getPrescriptionSchedule().getPrescription()), MAX_TEXT_LENGTH)
                + (bhp.hasMed()
                        ? ", <b>" + SYSTools.getAsHTML(bhp.getDose()) + " "
                                + DosageFormTools.getUsageText(
                                        bhp.getPrescription().getTradeForm().getDosageForm())
                                + "</b>"
                        : "")
                + BHPTools.getScheduleText(bhp, ", ", "")
                + (bhp.getPrescription().isWeightControlled()
                        ? " " + SYSConst.html_16x16_scales_internal + (bhp.isOpen() ? ""
                                : (bhp.getStockTransaction().isEmpty() ? " "
                                        : NumberFormat.getNumberInstance()
                                                .format(bhp.getStockTransaction().get(0).getWeight()) + "g "))
                        : "")
                + (bhp.getUser() != null ? ", <i>" + SYSTools.anonymizeUser(bhp.getUser().getUID()) + "</i>"
                        : "")
                + "</font></html>";
    }

    DefaultCPTitle cptitle = new DefaultCPTitle(title,
            OPDE.getAppInfo().isAllowedTo(InternalClassACL.UPDATE, internalClassID) ? applyActionListener
                    : null);

    JLabel icon1 = new JLabel(BHPTools.getIcon(bhp));
    icon1.setOpaque(false);
    if (!bhp.isOpen()) {
        icon1.setToolTipText(DateFormat.getDateTimeInstance().format(bhp.getIst()));
    }

    JLabel icon2 = new JLabel(BHPTools.getWarningIcon(bhp, stock));
    icon2.setOpaque(false);

    cptitle.getAdditionalIconPanel().add(icon1);
    cptitle.getAdditionalIconPanel().add(icon2);

    if (bhp.getPrescription().isClosed()) {
        JLabel icon3 = new JLabel(SYSConst.icon22stopSign);
        icon3.setOpaque(false);
        cptitle.getAdditionalIconPanel().add(icon3);
    }

    if (bhp.isOutcomeText()) {
        JLabel icon4 = new JLabel(SYSConst.icon22comment);
        icon4.setOpaque(false);
        cptitle.getAdditionalIconPanel().add(icon4);
    }

    if (!bhp.isOutcomeText() && bhp.getPrescriptionSchedule().getCheckAfterHours() != null) {
        JLabel icon4 = new JLabel(SYSConst.icon22intervalBySecond);
        icon4.setOpaque(false);
        cptitle.getAdditionalIconPanel().add(icon4);
    }

    if (OPDE.getAppInfo().isAllowedTo(InternalClassACL.UPDATE, internalClassID)) {
        if (!bhp.getPrescription().isClosed()) {

            /***
             *      _     _            _                _
             *     | |__ | |_ _ __    / \   _ __  _ __ | |_   _
             *     | '_ \| __| '_ \  / _ \ | '_ \| '_ \| | | | |
             *     | |_) | |_| | | |/ ___ \| |_) | |_) | | |_| |
             *     |_.__/ \__|_| |_/_/   \_\ .__/| .__/|_|\__, |
             *                             |_|   |_|      |___/
             */
            JButton btnApply = new JButton(SYSConst.icon22apply);
            btnApply.setPressedIcon(SYSConst.icon22applyPressed);
            btnApply.setAlignmentX(Component.RIGHT_ALIGNMENT);
            btnApply.setToolTipText(SYSTools.xx("nursingrecords.bhp.btnApply.tooltip"));
            btnApply.addActionListener(applyActionListener);
            btnApply.setContentAreaFilled(false);
            btnApply.setBorder(null);
            btnApply.setEnabled(bhp.isOpen()
                    && (!bhp.hasMed() || mapPrescription2Stock.containsKey(bhp.getPrescription())));
            cptitle.getRight().add(btnApply);

            /***
             *                             ____  _             _
             *       ___  _ __   ___ _ __ / ___|| |_ ___   ___| | __
             *      / _ \| '_ \ / _ \ '_ \\___ \| __/ _ \ / __| |/ /
             *     | (_) | |_) |  __/ | | |___) | || (_) | (__|   <
             *      \___/| .__/ \___|_| |_|____/ \__\___/ \___|_|\_\
             *           |_|
             */
            if (bhp.hasMed() && stock == null && MedInventoryTools.getNextToOpen(
                    TradeFormTools.getInventory4TradeForm(resident, bhp.getTradeForm())) != null) {
                final JButton btnOpenStock = new JButton(SYSConst.icon22ledGreenOn);
                btnOpenStock.setPressedIcon(SYSConst.icon22ledGreenOff);
                btnOpenStock.setAlignmentX(Component.RIGHT_ALIGNMENT);
                btnOpenStock.setContentAreaFilled(false);
                btnOpenStock.setBorder(null);
                btnOpenStock.setToolTipText(SYSTools
                        .toHTMLForScreen(SYSTools.xx("nursingrecords.inventory.stock.btnopen.tooltip")));
                btnOpenStock.addActionListener(new ActionListener() {
                    @Override
                    public void actionPerformed(ActionEvent actionEvent) {

                        EntityManager em = OPDE.createEM();
                        try {
                            em.getTransaction().begin();
                            em.lock(em.merge(resident), LockModeType.OPTIMISTIC);
                            BHP myBHP = em.merge(bhp);
                            em.lock(myBHP, LockModeType.OPTIMISTIC);
                            em.lock(myBHP.getPrescriptionSchedule(), LockModeType.OPTIMISTIC);
                            em.lock(myBHP.getPrescription(), LockModeType.OPTIMISTIC);

                            MedStock myStock = em.merge(MedInventoryTools.openNext(
                                    TradeFormTools.getInventory4TradeForm(resident, myBHP.getTradeForm())));
                            em.lock(myStock, LockModeType.OPTIMISTIC);
                            em.getTransaction().commit();

                            OPDE.getDisplayManager()
                                    .addSubMessage(new DisplayMessage(
                                            String.format(SYSTools.xx("newstocks.stock.has.been.opened"),
                                                    myStock.getID().toString())));
                            reload();

                        } catch (OptimisticLockException ole) {
                            OPDE.warn(ole);
                            if (em.getTransaction().isActive()) {
                                em.getTransaction().rollback();
                            }
                            if (ole.getMessage().indexOf("Class> entity.info.Resident") > -1) {
                                OPDE.getMainframe().emptyFrame();
                                OPDE.getMainframe().afterLogin();
                            }
                            OPDE.getDisplayManager().addSubMessage(DisplayManager.getLockMessage());
                        } catch (Exception e) {
                            if (em.getTransaction().isActive()) {
                                em.getTransaction().rollback();
                            }
                            OPDE.fatal(e);
                        } finally {
                            em.close();
                        }

                    }

                });
                cptitle.getRight().add(btnOpenStock);
            }

            if (!bhp.isOutcomeText()) {
                /***
                 *      _     _         ____       __
                 *     | |__ | |_ _ __ |  _ \ ___ / _|_   _ ___  ___
                 *     | '_ \| __| '_ \| |_) / _ \ |_| | | / __|/ _ \
                 *     | |_) | |_| | | |  _ <  __/  _| |_| \__ \  __/
                 *     |_.__/ \__|_| |_|_| \_\___|_|  \__,_|___/\___|
                 *
                 */
                final JButton btnRefuse = new JButton(SYSConst.icon22cancel);
                btnRefuse.setPressedIcon(SYSConst.icon22cancelPressed);
                btnRefuse.setAlignmentX(Component.RIGHT_ALIGNMENT);
                btnRefuse.setContentAreaFilled(false);
                btnRefuse.setBorder(null);
                btnRefuse.setToolTipText(
                        SYSTools.toHTMLForScreen(SYSTools.xx("nursingrecords.bhp.btnRefuse.tooltip")));
                btnRefuse.addActionListener(new ActionListener() {
                    @Override
                    public void actionPerformed(ActionEvent actionEvent) {
                        if (bhp.getState() != BHPTools.STATE_OPEN) {
                            return;
                        }

                        if (BHPTools.isChangeable(bhp)) {
                            EntityManager em = OPDE.createEM();
                            try {
                                em.getTransaction().begin();

                                em.lock(em.merge(resident), LockModeType.OPTIMISTIC);
                                BHP myBHP = em.merge(bhp);
                                em.lock(myBHP, LockModeType.OPTIMISTIC);
                                em.lock(myBHP.getPrescriptionSchedule(), LockModeType.OPTIMISTIC);
                                em.lock(myBHP.getPrescription(), LockModeType.OPTIMISTIC);

                                myBHP.setState(BHPTools.STATE_REFUSED);
                                myBHP.setUser(em.merge(OPDE.getLogin().getUser()));
                                myBHP.setIst(new Date());
                                myBHP.setiZeit(SYSCalendar.whatTimeIDIs(new Date()));
                                myBHP.setMDate(new Date());

                                mapBHP2Pane.put(myBHP, createCP4(myBHP));
                                int position = mapShift2BHP.get(myBHP.getShift()).indexOf(bhp);
                                mapShift2BHP.get(bhp.getShift()).remove(position);
                                mapShift2BHP.get(bhp.getShift()).add(position, myBHP);
                                if (myBHP.isOnDemand()) {
                                    Collections.sort(mapShift2BHP.get(myBHP.getShift()),
                                            BHPTools.getOnDemandComparator());
                                } else {
                                    Collections.sort(mapShift2BHP.get(myBHP.getShift()));
                                }

                                em.getTransaction().commit();
                                mapShift2Pane.put(myBHP.getShift(), createCP4(myBHP.getShift()));
                                buildPanel(false);
                            } catch (OptimisticLockException ole) {
                                OPDE.warn(ole);
                                if (em.getTransaction().isActive()) {
                                    em.getTransaction().rollback();
                                }
                                if (ole.getMessage().indexOf("Class> entity.info.Resident") > -1) {
                                    OPDE.getMainframe().emptyFrame();
                                    OPDE.getMainframe().afterLogin();
                                }
                                OPDE.getDisplayManager().addSubMessage(DisplayManager.getLockMessage());
                            } catch (Exception e) {
                                if (em.getTransaction().isActive()) {
                                    em.getTransaction().rollback();
                                }
                                OPDE.fatal(e);
                            } finally {
                                em.close();
                            }

                        } else {
                            OPDE.getDisplayManager().addSubMessage(
                                    new DisplayMessage(SYSTools.xx("nursingrecords.bhp.notchangeable")));
                        }
                    }
                });
                btnRefuse.setEnabled(!bhp.isOnDemand() && bhp.isOpen());
                cptitle.getRight().add(btnRefuse);

                /***
                 *      _     _         ____       __                ____  _                       _
                 *     | |__ | |_ _ __ |  _ \ ___ / _|_   _ ___  ___|  _ \(_)___  ___ __ _ _ __ __| |
                 *     | '_ \| __| '_ \| |_) / _ \ |_| | | / __|/ _ \ | | | / __|/ __/ _` | '__/ _` |
                 *     | |_) | |_| | | |  _ <  __/  _| |_| \__ \  __/ |_| | \__ \ (_| (_| | | | (_| |
                 *     |_.__/ \__|_| |_|_| \_\___|_|  \__,_|___/\___|____/|_|___/\___\__,_|_|  \__,_|
                 *
                 */
                final JButton btnRefuseDiscard = new JButton(SYSConst.icon22deleteall);
                btnRefuseDiscard.setPressedIcon(SYSConst.icon22deleteallPressed);
                btnRefuseDiscard.setAlignmentX(Component.RIGHT_ALIGNMENT);
                btnRefuseDiscard.setContentAreaFilled(false);
                btnRefuseDiscard.setBorder(null);
                btnRefuseDiscard.setToolTipText(
                        SYSTools.toHTMLForScreen(SYSTools.xx("nursingrecords.bhp.btnRefuseDiscard.tooltip")));
                btnRefuseDiscard.addActionListener(new ActionListener() {
                    @Override
                    public void actionPerformed(ActionEvent actionEvent) {
                        if (bhp.getState() != BHPTools.STATE_OPEN) {
                            return;
                        }

                        if (BHPTools.isChangeable(bhp)) {

                            if (bhp.getPrescription().isWeightControlled()) {
                                new DlgYesNo(SYSConst.icon48scales, new Closure() {
                                    @Override
                                    public void execute(Object o) {
                                        if (SYSTools.catchNull(o).isEmpty()) {
                                            weight = null;
                                        } else {
                                            weight = (BigDecimal) o;
                                        }
                                    }
                                }, "nursingrecords.bhp.weight", null, new Validator<BigDecimal>() {
                                    @Override
                                    public boolean isValid(String value) {
                                        BigDecimal bd = parse(value);
                                        return bd != null && bd.compareTo(BigDecimal.ZERO) > 0;

                                    }

                                    @Override
                                    public BigDecimal parse(String text) {
                                        return SYSTools.parseDecimal(text);
                                    }
                                });

                            }

                            if (bhp.getPrescription().isWeightControlled() && weight == null) {
                                OPDE.getDisplayManager().addSubMessage(new DisplayMessage(
                                        "nursingrecords.bhp.noweight.nosuccess", DisplayMessage.WARNING));
                                return;
                            }

                            EntityManager em = OPDE.createEM();
                            try {
                                em.getTransaction().begin();

                                em.lock(em.merge(resident), LockModeType.OPTIMISTIC);
                                BHP myBHP = em.merge(bhp);
                                em.lock(myBHP, LockModeType.OPTIMISTIC);
                                em.lock(myBHP.getPrescriptionSchedule(), LockModeType.OPTIMISTIC);
                                em.lock(myBHP.getPrescription(), LockModeType.OPTIMISTIC);

                                myBHP.setState(BHPTools.STATE_REFUSED_DISCARDED);
                                myBHP.setUser(em.merge(OPDE.getLogin().getUser()));
                                myBHP.setIst(new Date());
                                myBHP.setiZeit(SYSCalendar.whatTimeIDIs(new Date()));
                                myBHP.setMDate(new Date());

                                if (myBHP.shouldBeCalculated()) {
                                    MedInventory inventory = TradeFormTools.getInventory4TradeForm(resident,
                                            myBHP.getTradeForm());
                                    if (inventory != null) {
                                        MedInventoryTools.withdraw(em, em.merge(inventory), myBHP.getDose(),
                                                weight, myBHP);
                                    } else {
                                        OPDE.getDisplayManager().addSubMessage(
                                                new DisplayMessage("nursingrecords.bhp.NoInventory"));
                                    }
                                }

                                mapBHP2Pane.put(myBHP, createCP4(myBHP));
                                int position = mapShift2BHP.get(myBHP.getShift()).indexOf(bhp);
                                mapShift2BHP.get(bhp.getShift()).remove(position);
                                mapShift2BHP.get(bhp.getShift()).add(position, myBHP);
                                if (myBHP.isOnDemand()) {
                                    Collections.sort(mapShift2BHP.get(myBHP.getShift()),
                                            BHPTools.getOnDemandComparator());
                                } else {
                                    Collections.sort(mapShift2BHP.get(myBHP.getShift()));
                                }

                                em.getTransaction().commit();
                                mapShift2Pane.put(myBHP.getShift(), createCP4(myBHP.getShift()));
                                buildPanel(false);
                            } catch (OptimisticLockException ole) {
                                OPDE.warn(ole);
                                if (em.getTransaction().isActive()) {
                                    em.getTransaction().rollback();
                                }
                                if (ole.getMessage().indexOf("Class> entity.info.Resident") > -1) {
                                    OPDE.getMainframe().emptyFrame();
                                    OPDE.getMainframe().afterLogin();
                                }
                                OPDE.getDisplayManager().addSubMessage(DisplayManager.getLockMessage());
                            } catch (Exception e) {
                                if (em.getTransaction().isActive()) {
                                    em.getTransaction().rollback();
                                }
                                OPDE.fatal(e);
                            } finally {
                                em.close();
                            }

                        } else {
                            OPDE.getDisplayManager().addSubMessage(
                                    new DisplayMessage(SYSTools.xx("nursingrecords.bhp.notchangeable")));
                        }
                    }
                });

                btnRefuseDiscard.setEnabled(
                        !bhp.isOnDemand() && bhp.hasMed() && bhp.shouldBeCalculated() && bhp.isOpen());
                cptitle.getRight().add(btnRefuseDiscard);
            }

            /***
             *      _     _         _____                 _
             *     | |__ | |_ _ __ | ____|_ __ ___  _ __ | |_ _   _
             *     | '_ \| __| '_ \|  _| | '_ ` _ \| '_ \| __| | | |
             *     | |_) | |_| | | | |___| | | | | | |_) | |_| |_| |
             *     |_.__/ \__|_| |_|_____|_| |_| |_| .__/ \__|\__, |
             *                                     |_|        |___/
             */
            final JButton btnEmpty = new JButton(SYSConst.icon22empty);
            btnEmpty.setPressedIcon(SYSConst.icon22emptyPressed);
            btnEmpty.setAlignmentX(Component.RIGHT_ALIGNMENT);
            btnEmpty.setContentAreaFilled(false);
            btnEmpty.setBorder(null);
            btnEmpty.setToolTipText(SYSTools.xx("nursingrecords.bhp.btnEmpty.tooltip"));
            btnEmpty.addActionListener(new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent actionEvent) {
                    if (bhp.getState() == BHPTools.STATE_OPEN) {
                        return;
                    }

                    BHP outcomeBHP = BHPTools.getComment(bhp);

                    if (outcomeBHP != null && !outcomeBHP.isOpen()) {
                        // already commented
                        return;
                    }

                    if (BHPTools.isChangeable(bhp)) {
                        EntityManager em = OPDE.createEM();
                        try {
                            em.getTransaction().begin();

                            em.lock(em.merge(resident), LockModeType.OPTIMISTIC);
                            BHP myBHP = em.merge(bhp);

                            em.lock(myBHP, LockModeType.OPTIMISTIC);
                            em.lock(myBHP.getPrescriptionSchedule(), LockModeType.OPTIMISTIC);
                            em.lock(myBHP.getPrescription(), LockModeType.OPTIMISTIC);

                            // the normal BHPs (those assigned to a NursingProcess) are reset to the OPEN state.
                            // TXs are deleted
                            myBHP.setState(BHPTools.STATE_OPEN);
                            myBHP.setUser(null);
                            myBHP.setIst(null);
                            myBHP.setiZeit(null);
                            myBHP.setMDate(new Date());
                            myBHP.setText(null);

                            if (myBHP.shouldBeCalculated()) {
                                for (MedStockTransaction tx : myBHP.getStockTransaction()) {
                                    em.remove(tx);
                                }
                                myBHP.getStockTransaction().clear();
                            }

                            if (outcomeBHP != null) {
                                BHP myOutcomeBHP = em.merge(outcomeBHP);
                                em.remove(myOutcomeBHP);
                            }

                            if (myBHP.isOnDemand()) {
                                em.remove(myBHP);
                            }

                            em.getTransaction().commit();

                            if (myBHP.isOnDemand()) {
                                reload();
                            } else {

                                mapBHP2Pane.put(myBHP, createCP4(myBHP));
                                int position = mapShift2BHP.get(myBHP.getShift()).indexOf(bhp);
                                mapShift2BHP.get(bhp.getShift()).remove(position);
                                mapShift2BHP.get(bhp.getShift()).add(position, myBHP);
                                if (myBHP.isOnDemand()) {
                                    Collections.sort(mapShift2BHP.get(myBHP.getShift()),
                                            BHPTools.getOnDemandComparator());
                                } else {
                                    Collections.sort(mapShift2BHP.get(myBHP.getShift()));
                                }

                                mapShift2Pane.put(myBHP.getShift(), createCP4(myBHP.getShift()));
                                buildPanel(false);
                            }
                        } catch (OptimisticLockException ole) {
                            OPDE.warn(ole);
                            if (em.getTransaction().isActive()) {
                                em.getTransaction().rollback();
                            }
                            if (ole.getMessage().indexOf("Class> entity.info.Resident") > -1) {
                                OPDE.getMainframe().emptyFrame();
                                OPDE.getMainframe().afterLogin();
                            }
                            OPDE.getDisplayManager().addSubMessage(DisplayManager.getLockMessage());
                        } catch (Exception e) {
                            if (em.getTransaction().isActive()) {
                                em.getTransaction().rollback();
                            }
                            OPDE.fatal(e);
                        } finally {
                            em.close();
                        }

                    } else {
                        OPDE.getDisplayManager().addSubMessage(
                                new DisplayMessage(SYSTools.xx("nursingrecords.bhp.notchangeable")));
                    }
                }
            });
            btnEmpty.setEnabled(!bhp.isOpen());
            cptitle.getRight().add(btnEmpty);
        }

        /***
         *      _     _         ___        __
         *     | |__ | |_ _ __ |_ _|_ __  / _| ___
         *     | '_ \| __| '_ \ | || '_ \| |_ / _ \
         *     | |_) | |_| | | || || | | |  _| (_) |
         *     |_.__/ \__|_| |_|___|_| |_|_|  \___/
         *
         */
        final JButton btnInfo = new JButton(SYSConst.icon22info);

        btnInfo.setPressedIcon(SYSConst.icon22infoPressed);
        btnInfo.setAlignmentX(Component.RIGHT_ALIGNMENT);
        btnInfo.setContentAreaFilled(false);
        btnInfo.setBorder(null);
        btnInfo.setToolTipText(SYSTools.xx("nursingrecords.bhp.btnInfo.tooltip"));
        final JTextPane txt = new JTextPane();
        txt.setContentType("text/html");
        txt.setEditable(false);
        final JidePopup popupInfo = new JidePopup();
        popupInfo.setMovable(false);
        popupInfo.setContentPane(new JScrollPane(txt));
        popupInfo.removeExcludedComponent(txt);
        popupInfo.setDefaultFocusComponent(txt);

        btnInfo.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent actionEvent) {
                popupInfo.setOwner(btnInfo);

                if (bhp.isOutcomeText() && !bhp.isOpen()) {
                    txt.setText(SYSTools.toHTML(SYSConst.html_div(bhp.getText())));
                } else {
                    txt.setText(SYSTools.toHTML(SYSConst.html_div(bhp.getPrescription().getText())));
                }

                //                    txt.setText(SYSTools.toHTML(SYSConst.html_div(bhp.getPrescription().getText())));
                GUITools.showPopup(popupInfo, SwingConstants.SOUTH_WEST);
            }
        });

        if (bhp.isOutcomeText() && !bhp.isOpen()) {
            btnInfo.setEnabled(true);
        } else {
            btnInfo.setEnabled(!SYSTools.catchNull(bhp.getPrescription().getText()).isEmpty());
        }

        cptitle.getRight().add(btnInfo);

    }

    bhpPane.setTitleLabelComponent(cptitle.getMain());
    bhpPane.setSlidingDirection(SwingConstants.SOUTH);

    final JTextPane contentPane = new JTextPane();
    contentPane.setEditable(false);
    contentPane.setContentType("text/html");
    bhpPane.setContentPane(contentPane);
    bhpPane.setBackground(bhp.getBG());
    bhpPane.setForeground(bhp.getFG());

    try {
        bhpPane.setCollapsed(true);
    } catch (PropertyVetoException e) {
        OPDE.error(e);
    }

    bhpPane.addCollapsiblePaneListener(new CollapsiblePaneAdapter() {
        @Override
        public void paneExpanded(CollapsiblePaneEvent collapsiblePaneEvent) {
            contentPane.setText(SYSTools.toHTML(
                    PrescriptionTools.getPrescriptionAsHTML(bhp.getPrescription(), false, false, true, false)));
        }
    });

    bhpPane.setHorizontalAlignment(SwingConstants.LEADING);
    bhpPane.setOpaque(false);
    return bhpPane;
}