text:
// Copyright (c) 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/chromeos/login/user_image_manager_impl.h"

#include "base/bind.h"
#include "base/command_line.h"
#include "base/debug/trace_event.h"
#include "base/file_util.h"
#include "base/files/file_path.h"
#include "base/logging.h"
#include "base/metrics/histogram.h"
#include "base/path_service.h"
#include "base/prefs/pref_registry_simple.h"
#include "base/prefs/pref_service.h"
#include "base/rand_util.h"
#include "base/threading/worker_pool.h"
#include "base/time/time.h"
#include "base/values.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/chrome_notification_types.h"
#include "chrome/browser/chromeos/login/default_user_images.h"
#include "chrome/browser/chromeos/login/helper.h"
#include "chrome/browser/chromeos/login/user_image.h"
#include "chrome/browser/chromeos/login/user_image_sync_observer.h"
#include "chrome/browser/chromeos/login/user_manager.h"
#include "chrome/browser/prefs/scoped_user_pref_update.h"
#include "chrome/browser/profiles/profile_downloader.h"
#include "chrome/browser/profiles/profile_manager.h"
#include "chrome/common/chrome_paths.h"
#include "chrome/common/chrome_switches.h"
#include "chromeos/chromeos_switches.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/notification_service.h"
#include "content/public/common/url_constants.h"
#include "ui/gfx/image/image_skia.h"
#include "ui/webui/web_ui_util.h"

using content::BrowserThread;

namespace chromeos {

namespace {

// A dictionary that maps usernames to old user image data with images stored in
// PNG format. Deprecated.
// TODO(ivankr): remove this const char after migration is gone.
const char kUserImages[] = "UserImages";

// A dictionary that maps usernames to user image data with images stored in
// JPEG format.
const char kUserImageProperties[] = "user_image_info";

// Names of user image properties.
const char kImagePathNodeName[] = "path";
const char kImageIndexNodeName[] = "index";
const char kImageURLNodeName[] = "url";

// Delay betweeen user login and user image migration.
const int kUserImageMigrationDelaySec = 50;

// Delay betweeen user login and attempt to update user's profile data.
const int kProfileDataDownloadDelaySec = 10;

// Interval betweeen retries to update user's profile data.
const int kProfileDataDownloadRetryIntervalSec = 300;

// Delay betweeen subsequent profile refresh attempts (24 hrs).
const int kProfileRefreshIntervalSec = 24 * 3600;

const char kSafeImagePathExtension[] = ".jpg";

// Enum for reporting histograms about profile picture download.
enum ProfileDownloadResult {
  kDownloadSuccessChanged,
  kDownloadSuccess,
  kDownloadFailure,
  kDownloadDefault,
  kDownloadCached,

  // Must be the last, convenient count.
  kDownloadResultsCount
};

// Time histogram prefix for a cached profile image download.
const char kProfileDownloadCachedTime[] =
    "UserImage.ProfileDownloadTime.Cached";
// Time histogram prefix for the default profile image download.
const char kProfileDownloadDefaultTime[] =
    "UserImage.ProfileDownloadTime.Default";
// Time histogram prefix for a failed profile image download.
const char kProfileDownloadFailureTime[] =
    "UserImage.ProfileDownloadTime.Failure";
// Time histogram prefix for a successful profile image download.
const char kProfileDownloadSuccessTime[] =
    "UserImage.ProfileDownloadTime.Success";
// Time histogram suffix for a profile image download after login.
const char kProfileDownloadReasonLoggedIn[] = "LoggedIn";
// Time histogram suffix for a scheduled profile image download.
const char kProfileDownloadReasonScheduled[] = "Scheduled";
// Time histogram suffix for a profile image download retry.
const char kProfileDownloadReasonRetry[] = "Retry";

// Add a histogram showing the time it takes to download a profile image.
// Separate histograms are reported for each download |reason| and |result|.
void AddProfileImageTimeHistogram(ProfileDownloadResult result,
                                  const std::string& download_reason,
                                  const base::TimeDelta& time_delta) {
  std::string histogram_name;
  switch (result) {
    case kDownloadFailure:
      histogram_name = kProfileDownloadFailureTime;
      break;
    case kDownloadDefault:
      histogram_name = kProfileDownloadDefaultTime;
      break;
    case kDownloadSuccess:
      histogram_name = kProfileDownloadSuccessTime;
      break;
    case kDownloadCached:
      histogram_name = kProfileDownloadCachedTime;
      break;
    default:
      NOTREACHED();
  }
  if (!download_reason.empty()) {
    histogram_name += ".";
    histogram_name += download_reason;
  }

  static const base::TimeDelta min_time = base::TimeDelta::FromMilliseconds(1);
  static const base::TimeDelta max_time = base::TimeDelta::FromSeconds(50);
  const size_t bucket_count(50);

  base::HistogramBase* counter = base::Histogram::FactoryTimeGet(
      histogram_name, min_time, max_time, bucket_count,
      base::HistogramBase::kUmaTargetedHistogramFlag);
  counter->AddTime(time_delta);

  DVLOG(1) << "Profile image download time: " << time_delta.InSecondsF();
}

// Deletes image file.
void DeleteImageFile(const std::string& image_path) {
  if (image_path.empty())
    return;
  base::FilePath fp(image_path);
  BrowserThread::PostTask(
      BrowserThread::FILE,
      FROM_HERE,
      base::Bind(base::IgnoreResult(&base::DeleteFile),
                 fp, /* recursive= */  false));
}

// Converts |image_index| to UMA histogram value.
int ImageIndexToHistogramIndex(int image_index) {
  switch (image_index) {
    case User::kExternalImageIndex:
      // TODO(ivankr): Distinguish this from selected from file.
      return kHistogramImageFromCamera;
    case User::kProfileImageIndex:
      return kHistogramImageFromProfile;
    default:
      return image_index;
  }
}

}  // namespace

// static
int UserImageManagerImpl::user_image_migration_delay_sec =
    kUserImageMigrationDelaySec;

// static
void UserImageManager::RegisterPrefs(PrefRegistrySimple* registry) {
  registry->RegisterDictionaryPref(kUserImages);
  registry->RegisterDictionaryPref(kUserImageProperties);
}

UserImageManagerImpl::UserImageManagerImpl()
    : image_loader_(new UserImageLoader(ImageDecoder::ROBUST_JPEG_CODEC)),
      unsafe_image_loader_(new UserImageLoader(ImageDecoder::DEFAULT_CODEC)),
      last_image_set_async_(false),
      downloaded_profile_image_data_url_(content::kAboutBlankURL),
      downloading_profile_image_(false),
      migrate_current_user_on_load_(false) {
}

UserImageManagerImpl::~UserImageManagerImpl() {
}

void UserImageManagerImpl::LoadUserImages(const UserList& users) {
  PrefService* local_state = g_browser_process->local_state();
  const DictionaryValue* prefs_images_unsafe =
      local_state->GetDictionary(kUserImages);
  const DictionaryValue* prefs_images =
      local_state->GetDictionary(kUserImageProperties);
  if (!prefs_images && !prefs_images_unsafe)
    return;

  for (UserList::const_iterator it = users.begin(); it != users.end(); ++it) {
    User* user = *it;
    const base::DictionaryValue* image_properties = NULL;
    bool needs_migration = false;  // |true| if user has image in old format.
    bool safe_source = false;      // |true| if image loaded from safe source.

    if (prefs_images_unsafe) {
      needs_migration = prefs_images_unsafe->GetDictionaryWithoutPathExpansion(
          user->email(), &image_properties);
    }
    if (prefs_images) {
      safe_source = prefs_images->GetDictionaryWithoutPathExpansion(
          user->email(), &image_properties);
    }

    if (needs_migration)
      users_to_migrate_.insert(user->email());

    if (!image_properties) {
      SetInitialUserImage(user->email());
    } else {
      int image_index = User::kInvalidImageIndex;
      image_properties->GetInteger(kImageIndexNodeName, &image_index);

      if (image_index >= 0 && image_index < kDefaultImagesCount) {
        user->SetImage(UserImage(GetDefaultImage(image_index)),
                       image_index);
      } else if (image_index == User::kExternalImageIndex ||
                 image_index == User::kProfileImageIndex) {
        std::string image_path;
        image_properties->GetString(kImagePathNodeName, &image_path);
        // Path may be empty for profile images (meaning that the image
        // hasn't been downloaded for the first time yet, in which case a
        // download will be scheduled for |kProfileDataDownloadDelayMs|
        // after user logs in).
        DCHECK(!image_path.empty() || image_index == User::kProfileImageIndex);
        std::string image_url;
        image_properties->GetString(kImageURLNodeName, &image_url);
        GURL image_gurl(image_url);
        // Until image has been loaded, use the stub image (gray avatar).
        user->SetStubImage(image_index, true);
        user->SetImageURL(image_gurl);
        if (!image_path.empty()) {
          if (needs_migration) {
            // Non-JPG image will be migrated once user logs in.
            // Stub image will be used for now. Continue with other users.
            continue;
          }
          DCHECK(safe_source);
          if (!safe_source)
            continue;

          // Load user image asynchronously - at this point we are able to use
          // JPEG image loaded since image comes from safe pref source
          // i.e. converted to JPEG.
          image_loader_->Start(
              image_path, 0  /* no resize */,
              base::Bind(&UserImageManagerImpl::SetUserImage,
                         base::Unretained(this),
                         user->email(), image_index, image_gurl));
        }
      } else {
        NOTREACHED();
      }
    }
  }
}

void UserImageManagerImpl::UserLoggedIn(const std::string& email,
                                        bool user_is_new,
                                        bool user_is_local) {
  User* user = UserManager::Get()->GetLoggedInUser();
  if (user_is_new) {
    if (!user_is_local)
      SetInitialUserImage(email);
  } else {
    if (!user_is_local) {
      // If current user image is profile image, it needs to be refreshed.
      bool download_profile_image =
          user->image_index() == User::kProfileImageIndex;
      if (download_profile_image)
        InitDownloadedProfileImage();

      // Download user's profile data (full name and optionally image) to see if
      // it has changed.
      BrowserThread::PostDelayedTask(
          BrowserThread::UI,
          FROM_HERE,
          base::Bind(&UserImageManagerImpl::DownloadProfileData,
                     base::Unretained(this),
                     kProfileDownloadReasonLoggedIn,
                     download_profile_image),
          base::TimeDelta::FromSeconds(kProfileDataDownloadDelaySec));
    }

    UMA_HISTOGRAM_ENUMERATION("UserImage.LoggedIn",
                              ImageIndexToHistogramIndex(user->image_index()),
                              kHistogramImagesCount);

    if (users_to_migrate_.count(email)) {
      const DictionaryValue* prefs_images_unsafe =
          g_browser_process->local_state()->GetDictionary(kUserImages);
      const base::DictionaryValue* image_properties = NULL;
      if (prefs_images_unsafe->GetDictionaryWithoutPathExpansion(
              user->email(), &image_properties)) {
        std::string image_path;
        image_properties->GetString(kImagePathNodeName, &image_path);
        if (!image_path.empty()) {
          // User needs image format migration but
          // first we need to load and decode that image.
          LOG(INFO) << "Waiting for user image to load before migration";
          migrate_current_user_on_load_ = true;
              unsafe_image_loader_->Start(
                  image_path, 0  /* no resize */,
                  base::Bind(&UserImageManagerImpl::SetUserImage,
                             base::Unretained(this),
                             user->email(),
                             user->image_index(),
                             user->image_url()));
        } else {
          // Otherwise migrate user image properties right away.
          BrowserThread::PostDelayedTask(
              BrowserThread::UI,
              FROM_HERE,
              base::Bind(&UserImageManagerImpl::MigrateUserImage,
                         base::Unretained(this)),
              base::TimeDelta::FromSeconds(user_image_migration_delay_sec));
        }
      }
    }
  }

  if (!user_is_local) {
    // Set up a repeating timer for refreshing the profile data.
    profile_download_timer_.Start(
        FROM_HERE, base::TimeDelta::FromSeconds(kProfileRefreshIntervalSec),
        this, &UserImageManagerImpl::DownloadProfileDataScheduled);
  }
  CommandLine* command_line = CommandLine::ForCurrentProcess();
  if (user->CanSyncImage() &&
      !command_line->HasSwitch(chromeos::switches::kDisableUserImageSync)) {
    if (user_image_sync_observer_.get() &&
        !command_line->HasSwitch(::switches::kMultiProfiles))
      NOTREACHED() << "User logged in second time.";
    user_image_sync_observer_.reset(new UserImageSyncObserver(user));
  }
}

void UserImageManagerImpl::SaveUserDefaultImageIndex(
    const std::string& username,
    int image_index) {
  DCHECK(image_index >= 0 && image_index < kDefaultImagesCount);
  SetUserImage(username, image_index, GURL(),
               UserImage(GetDefaultImage(image_index)));
  SaveImageToLocalState(username, "", image_index, GURL(), false);
}

void UserImageManagerImpl::SaveUserImage(const std::string& username,
                                         const UserImage& user_image) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
  SaveUserImageInternal(username, User::kExternalImageIndex,
                        GURL(), user_image);
}

void UserImageManagerImpl::SaveUserImageFromFile(const std::string& username,
                                                 const base::FilePath& path) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
  // Always use unsafe image loader because we resize the image when saving
  // anyway.
  unsafe_image_loader_->Start(
      path.value(), login::kMaxUserImageSize,
      base::Bind(&UserImageManagerImpl::SaveUserImage,
                 base::Unretained(this), username));
}

void UserImageManagerImpl::SaveUserImageFromProfileImage(
    const std::string& username) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
  if (!downloaded_profile_image_.isNull()) {
    // Profile image has already been downloaded, so save it to file right now.
    DCHECK(profile_image_url_.is_valid());
    SaveUserImageInternal(
        username,
        User::kProfileImageIndex, profile_image_url_,
        UserImage::CreateAndEncode(downloaded_profile_image_));
  } else {
    // No profile image - use the stub image (gray avatar).
    SetUserImage(username, User::kProfileImageIndex, GURL(), UserImage());
    SaveImageToLocalState(username, "", User::kProfileImageIndex,
                          GURL(), false);
  }
}

void UserImageManagerImpl::DeleteUserImage(const std::string& username) {
  // Delete from the old dictionary, if present.
  DeleteOldUserImage(username);

  PrefService* prefs = g_browser_process->local_state();
  DictionaryPrefUpdate prefs_images_update(prefs, kUserImageProperties);
  const base::DictionaryValue* image_properties;
  if (prefs_images_update->GetDictionaryWithoutPathExpansion(
          username, &image_properties)) {
    std::string image_path;
    image_properties->GetString(kImageURLNodeName, &image_path);
    prefs_images_update->RemoveWithoutPathExpansion(username, NULL);
    DeleteImageFile(image_path);
  }
}

void UserImageManagerImpl::DownloadProfileImage(const std::string& reason) {
  DownloadProfileData(reason, true);
}

UserImageSyncObserver* UserImageManagerImpl::GetSyncObserver() const {
  return user_image_sync_observer_.get();
}

void UserImageManagerImpl::Shutdown() {
  profile_image_downloader_.reset();
  user_image_sync_observer_.reset();
}

const gfx::ImageSkia& UserImageManagerImpl::DownloadedProfileImage() const {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
  return downloaded_profile_image_;
}

base::FilePath UserImageManagerImpl::GetImagePathForUser(
    const std::string& username) {
  std::string filename = username + kSafeImagePathExtension;
  base::FilePath user_data_dir;
  PathService::Get(chrome::DIR_USER_DATA, &user_data_dir);
  return user_data_dir.AppendASCII(filename);
}

void UserImageManagerImpl::SetInitialUserImage(const std::string& username) {
  // Choose a random default image.
  int image_id =
      base::RandInt(kFirstDefaultImageIndex, kDefaultImagesCount - 1);
  SaveUserDefaultImageIndex(username, image_id);
}

void UserImageManagerImpl::SetUserImage(const std::string& username,
                                        int image_index,
                                        const GURL& image_url,
                                        const UserImage& user_image) {
  User* user = const_cast<User*>(UserManager::Get()->FindUser(username));
  // User may have been removed by now.
  if (user) {
    bool image_changed = user->image_index() != User::kInvalidImageIndex;
    bool is_current_user = user == UserManager::Get()->GetLoggedInUser();
    if (!user_image.image().isNull())
      user->SetImage(user_image, image_index);
    else
      user->SetStubImage(image_index, false);
    user->SetImageURL(image_url);
    // For the logged-in user with a profile picture, initialize
    // |downloaded_profile_picture_|.
    if (is_current_user && image_index == User::kProfileImageIndex) {
      InitDownloadedProfileImage();
    }
    if (image_changed) {
      // Unless this is first-time setting with |SetInitialUserImage|,
      // send a notification about image change.
      content::NotificationService::current()->Notify(
          chrome::NOTIFICATION_LOGIN_USER_IMAGE_CHANGED,
          content::Source<UserImageManager>(this),
          content::Details<const User>(user));
    }
    if (is_current_user && migrate_current_user_on_load_)
      MigrateUserImage();
  }
}

void UserImageManagerImpl::SaveUserImageInternal(const std::string& username,
                                                 int image_index,
                                                 const GURL& image_url,
                                                 const UserImage& user_image) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));

  SetUserImage(username, image_index, image_url, user_image);

  // Ignore if data stored or cached outside the user's cryptohome is to be
  // treated as ephemeral.
  if (UserManager::Get()->IsUserNonCryptohomeDataEphemeral(username))
    return;

  base::FilePath image_path = GetImagePathForUser(username);
  DVLOG(1) << "Saving user image to " << image_path.value();

  last_image_set_async_ = true;

  base::WorkerPool::PostTask(
      FROM_HERE,
      base::Bind(&UserImageManagerImpl::SaveImageToFile,
                 base::Unretained(this),
                 username, user_image, image_path, image_index, image_url),
      /* is_slow= */ false);
}

void UserImageManagerImpl::SaveImageToFile(const std::string& username,
                                           const UserImage& user_image,
                                           const base::FilePath& image_path,
                                           int image_index,
                                           const GURL& image_url) {
  if (!SaveBitmapToFile(user_image, image_path))
    return;

  BrowserThread::PostTask(
      BrowserThread::UI,
      FROM_HERE,
      base::Bind(&UserImageManagerImpl::SaveImageToLocalState,
                 base::Unretained(this),
                 username, image_path.value(), image_index, image_url, true));
}

void UserImageManagerImpl::SaveImageToLocalState(const std::string& username,
                                                 const std::string& image_path,
                                                 int image_index,
                                                 const GURL& image_url,
                                                 bool is_async) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));

  // Ignore if data stored or cached outside the user's cryptohome is to be
  // treated as ephemeral.
  if (UserManager::Get()->IsUserNonCryptohomeDataEphemeral(username))
    return;

  // TODO(ivankr): use unique filenames for user images each time
  // a new image is set so that only the last image update is saved
  // to Local State and notified.
  if (is_async && !last_image_set_async_) {
    DVLOG(1) << "Ignoring saved image because it has changed";
    return;
  } else if (!is_async) {
    // Reset the async image save flag if called directly from the UI thread.
    last_image_set_async_ = false;
  }

  PrefService* local_state = g_browser_process->local_state();
  DictionaryPrefUpdate images_update(local_state, kUserImageProperties);
  base::DictionaryValue* image_properties = new base::DictionaryValue();
  image_properties->Set(kImagePathNodeName, new StringValue(image_path));
  image_properties->Set(kImageIndexNodeName,
                        new base::FundamentalValue(image_index));
  if (!image_url.is_empty()) {
    image_properties->Set(kImageURLNodeName,
                          new StringValue(image_url.spec()));
  } else {
    image_properties->Remove(kImageURLNodeName, NULL);
  }
  images_update->SetWithoutPathExpansion(username, image_properties);
  DVLOG(1) << "Saving path to user image in Local State.";

  if (users_to_migrate_.count(username)) {
    DeleteOldUserImage(username);
    users_to_migrate_.erase(username);
  }

  UserManager::Get()->NotifyLocalStateChanged();
}

bool UserImageManagerImpl::SaveBitmapToFile(const UserImage& user_image,
                                            const base::FilePath& image_path) {
  UserImage safe_image;
  const UserImage::RawImage* encoded_image = NULL;
  if (!user_image.is_safe_format()) {
    safe_image = UserImage::CreateAndEncode(user_image.image());
    encoded_image = &safe_image.raw_image();
    UMA_HISTOGRAM_MEMORY_KB("UserImage.RecodedJpegSize", encoded_image->size());
  } else if (user_image.has_raw_image()) {
    encoded_image = &user_image.raw_image();
  } else {
    NOTREACHED() << "Raw image missing.";
    return false;
  }

  if (file_util::WriteFile(image_path,
                           reinterpret_cast<const char*>(&(*encoded_image)[0]),
                           encoded_image->size()) == -1) {
    LOG(ERROR) << "Failed to save image to file.";
    return false;
  }
  return true;
}

void UserImageManagerImpl::InitDownloadedProfileImage() {
  const User* logged_in_user = UserManager::Get()->GetLoggedInUser();
  DCHECK_EQ(logged_in_user->image_index(), User::kProfileImageIndex);
  if (downloaded_profile_image_.isNull() && !logged_in_user->image_is_stub()) {
    VLOG(1) << "Profile image initialized";
    downloaded_profile_image_ = logged_in_user->image();
    downloaded_profile_image_data_url_ =
        webui::GetBitmapDataUrl(*downloaded_profile_image_.bitmap());
    profile_image_url_ = logged_in_user->image_url();
  }
}

void UserImageManagerImpl::DownloadProfileData(const std::string& reason,
                                               bool download_image) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));

  // GAIA profiles exist for regular users only.
  if (!UserManager::Get()->IsLoggedInAsRegularUser())
    return;

  // Mark profile picture as needed.
  downloading_profile_image_ |= download_image;

  // Another download is already in progress
  if (profile_image_downloader_.get())
    return;

  profile_image_download_reason_ = reason;
  profile_image_load_start_time_ = base::Time::Now();
  profile_image_downloader_.reset(new ProfileDownloader(this));
  profile_image_downloader_->Start();
}

void UserImageManagerImpl::DownloadProfileDataScheduled() {
  const User* logged_in_user = UserManager::Get()->GetLoggedInUser();
  // If current user image is profile image, it needs to be refreshed.
  bool download_profile_image =
      logged_in_user->image_index() == User::kProfileImageIndex;
  DownloadProfileData(kProfileDownloadReasonScheduled, download_profile_image);
}

void UserImageManagerImpl::DownloadProfileDataRetry(bool download_image) {
  DownloadProfileData(kProfileDownloadReasonRetry, download_image);
}

// ProfileDownloaderDelegate override.
bool UserImageManagerImpl::NeedsProfilePicture() const {
  return downloading_profile_image_;
}

// ProfileDownloaderDelegate override.
int UserImageManagerImpl::GetDesiredImageSideLength() const {
  return GetCurrentUserImageSize();
}

// ProfileDownloaderDelegate override.
std::string UserImageManagerImpl::GetCachedPictureURL() const {
  return profile_image_url_.spec();
}

Profile* UserImageManagerImpl::GetBrowserProfile() {
  return ProfileManager::GetDefaultProfile();
}

void UserImageManagerImpl::OnProfileDownloadSuccess(
    ProfileDownloader* downloader) {
  // Make sure that |ProfileDownloader| gets deleted after return.
  scoped_ptr<ProfileDownloader> profile_image_downloader(
      profile_image_downloader_.release());
  DCHECK_EQ(downloader, profile_image_downloader.get());

  UserManager* user_manager = UserManager::Get();
  const User* user = user_manager->GetLoggedInUser();

  if (!downloader->GetProfileFullName().empty()) {
    user_manager->SaveUserDisplayName(
        user->email(), downloader->GetProfileFullName());
  }

  bool requested_image = downloading_profile_image_;
  downloading_profile_image_ = false;
  if (!requested_image)
    return;

  ProfileDownloadResult result = kDownloadFailure;
  switch (downloader->GetProfilePictureStatus()) {
    case ProfileDownloader::PICTURE_SUCCESS:
      result = kDownloadSuccess;
      break;
    case ProfileDownloader::PICTURE_CACHED:
      result = kDownloadCached;
      break;
    case ProfileDownloader::PICTURE_DEFAULT:
      result = kDownloadDefault;
      break;
    default:
      NOTREACHED();
  }

  UMA_HISTOGRAM_ENUMERATION("UserImage.ProfileDownloadResult",
      result, kDownloadResultsCount);

  DCHECK(!profile_image_load_start_time_.is_null());
  base::TimeDelta delta = base::Time::Now() - profile_image_load_start_time_;
  AddProfileImageTimeHistogram(result, profile_image_download_reason_, delta);

  if (result == kDownloadDefault) {
    content::NotificationService::current()->Notify(
        chrome::NOTIFICATION_PROFILE_IMAGE_UPDATE_FAILED,
        content::Source<UserImageManager>(this),
        content::NotificationService::NoDetails());
  }

  // Nothing to do if picture is cached or the default avatar.
  if (result != kDownloadSuccess)
    return;

  // Check if this image is not the same as already downloaded.
  SkBitmap new_bitmap(downloader->GetProfilePicture());
  std::string new_image_data_url = webui::GetBitmapDataUrl(new_bitmap);
  if (!downloaded_profile_image_data_url_.empty() &&
      new_image_data_url == downloaded_profile_image_data_url_)
    return;

  downloaded_profile_image_data_url_ = new_image_data_url;
  downloaded_profile_image_ = gfx::ImageSkia::CreateFrom1xBitmap(
      downloader->GetProfilePicture());
  profile_image_url_ = GURL(downloader->GetProfilePictureURL());

  if (user->image_index() == User::kProfileImageIndex) {
    VLOG(1) << "Updating profile image for logged-in user";
    UMA_HISTOGRAM_ENUMERATION("UserImage.ProfileDownloadResult",
                              kDownloadSuccessChanged,
                              kDownloadResultsCount);
    // This will persist |downloaded_profile_image_| to file.
    SaveUserImageFromProfileImage(user->email());
  }

  content::NotificationService::current()->Notify(
      chrome::NOTIFICATION_PROFILE_IMAGE_UPDATED,
      content::Source<UserImageManager>(this),
      content::Details<const gfx::ImageSkia>(&downloaded_profile_image_));
}

void UserImageManagerImpl::OnProfileDownloadFailure(
    ProfileDownloader* downloader,
    ProfileDownloaderDelegate::FailureReason reason) {
  DCHECK_EQ(downloader, profile_image_downloader_.get());
  profile_image_downloader_.reset();

  UMA_HISTOGRAM_ENUMERATION("UserImage.ProfileDownloadResult",
      kDownloadFailure, kDownloadResultsCount);

  DCHECK(!profile_image_load_start_time_.is_null());
  base::TimeDelta delta = base::Time::Now() - profile_image_load_start_time_;
  AddProfileImageTimeHistogram(kDownloadFailure, profile_image_download_reason_,
                               delta);

  // Retry download after some time if a network error has occured.
  if (reason == ProfileDownloaderDelegate::NETWORK_ERROR) {
    BrowserThread::PostDelayedTask(
        BrowserThread::UI,
        FROM_HERE,
        base::Bind(&UserImageManagerImpl::DownloadProfileDataRetry,
                   base::Unretained(this),
                   downloading_profile_image_),
        base::TimeDelta::FromSeconds(kProfileDataDownloadRetryIntervalSec));
  }

  downloading_profile_image_ = false;

  content::NotificationService::current()->Notify(
      chrome::NOTIFICATION_PROFILE_IMAGE_UPDATE_FAILED,
      content::Source<UserImageManager>(this),
      content::NotificationService::NoDetails());
}

void UserImageManagerImpl::MigrateUserImage() {
  User* user = UserManager::Get()->GetLoggedInUser();
  if (user->image_is_loading()) {
    LOG(INFO) << "Waiting for user image to load before migration";
    migrate_current_user_on_load_ = true;
    return;
  }
  migrate_current_user_on_load_ = false;
  if (user->has_raw_image() && user->image_is_safe_format()) {
    // Nothing to migrate already, make sure we delete old image.
    DeleteOldUserImage(user->email());
    users_to_migrate_.erase(user->email());
    return;
  }
  if (user->HasDefaultImage()) {
    SaveUserDefaultImageIndex(user->email(), user->image_index());
  } else {
    SaveUserImageInternal(user->email(), user->image_index(),
                          user->image_url(), user->user_image());
  }
  UMA_HISTOGRAM_ENUMERATION("UserImage.Migration",
                            ImageIndexToHistogramIndex(user->image_index()),
                            kHistogramImagesCount);
}

void UserImageManagerImpl::DeleteOldUserImage(const std::string& username) {
  PrefService* prefs = g_browser_process->local_state();
  DictionaryPrefUpdate prefs_images_update(prefs, kUserImages);
  const base::DictionaryValue* image_properties;
  if (prefs_images_update->GetDictionaryWithoutPathExpansion(
          username, &image_properties)) {
    std::string image_path;
    image_properties->GetString(kImagePathNodeName, &image_path);
    prefs_images_update->RemoveWithoutPathExpansion(username, NULL);
    DeleteImageFile(image_path);
  }
}

}  // namespace chromeos


text:
ACCEPTED

#### According to
Index Fungorum

#### Published in
Karstenia 26(2): 47 (1986)

#### Original name
Pseudombrophila maekinenii Harmaja

### Remarks
null

redpajama_set_name: RedPajamaGithub


text:
* default to async_supported: true style servlet configuration
* assume thread-safe by default and avoid config.threadsafe! detection in Rails
* improve JRuby 9K compatibility - avoid setting jruby.compat.version parameter
* fixes for rolling reload when context has been destroyed (see #133)
* support setting `:keystore_type` (for SSL)
* handle auto-loading of `Trinidad::CommandLineParser` constant for init service's
  Trinidad::Daemon backwards-compatibility (see https://github.com/trinidad/trinidad_init_services/issues/41)
* support for running fully on Java 8 (class sun.security.tools.KeyTool seems gone)

## trinidad_jars 1.5.1 (2016-07-14)

* adjust (timer) cleanup for newer pg-jdbc driver versions

* Tomcat 7.0.64 http://tomcat.apache.org/tomcat-7.0-doc/changelog.html

## trinidad_jars 1.5.0 (2016-07-13)

* Tomcat 7.0.59 http://tomcat.apache.org/tomcat-7.0-doc/changelog.html

## trinidad_jars 1.4.2 (2015-10-09)

* Tomcat 7.0.57 http://tomcat.apache.org/tomcat-7.0-doc/changelog.html
* workaround for postgres reloading issues (#133) ... affects 1.5.0 only

## trinidad_jars 1.4.1 (2015-05-01)

* Tomcat 7.0.55 https://tomcat.apache.org/security-7.html#Fixed_in_Apache_Tomcat_7.0.55
  fixes multiple security vulnerabilities since Tomcat 7.0.50
* do potential MariaDB driver cleanup (an alternative for the MySQL official driver)
* updated JarScanner internals so we're compilant

## trinidad 1.5.0.B1 (2014-03-27)

* make sure the context.name does include the original name (after reload)
* set server's parent class-loader for Class.forName to work better from Tomcat
* add the jruby-rack.jar to web-app's loader instead of using the one loaded
* start defaulting to "sensible" thread-safe
  in development/test mode we shall start the same (thread-safe) way as in production
* set "reloader" (java) thread name for easier identification
* handle `address: *` since it broke on adding a JMX bean with * in it's name (which is not a valid one)
* allow to disable (inherited) web-app extension by specifying `name: false`
* remove some of the 'old' deprecated methods/configuration
* class-loader kung-fu to get thigns right towards leak free applications ...
* we're still binding by default to localhost/127.0.0.1 - change to '*' (#119)

## trinidad_jars 1.4.0 (2014-03-27)

* Tomcat 7.0.50 http://tomcat.apache.org/tomcat-7.0-doc/changelog.html
* a "faster" (default) JarScanner for Rack/Rails applications
  based on TC's StandardJarScanner implementation
* an extended (default) web-app loader (to handle JRuby specific "leaks")
  as well as some MySQL and PostgreSQL JDBC driver specific cleanup

## trinidad 1.4.6 (2013-12-28)

* default :port for SSL is now (a more Ruby-sh) 3443
* better handling of port/address for connectors when only one configured
  e.g. in case users want https:// but not http://
* make sure we keep localhost for tomcat.server's address if not specified
* only generate 'dummy' SSL keystore if not done already (#117)
* getting more "strict" about jruby-rack and trinidad_jars gem versions
* use a custom context manager to have "truly" lazy (java) sessions (#123)
* improve thread-safe rails (4.0) auto-detection from environment files
* allow symlinks in assets FS e.g. public/assets might be linked (#120)
* do not default to 'localhost' (default host) but use :address as name (#119)

with (latest) JRuby-Rack 1.1.13 works on JRuby 1.6.x (even < 1.6.7) as well

## trinidad_jars 1.3.0 (2013-12-28)

* Tomcat 7.0.47 http://tomcat.apache.org/tomcat-7.0-doc/changelog.html
* re-invent the MessageFormatter in Java
* introduce Jerry - Tomcat's little companion

## trinidad_jars 1.2.5 (2013-11-14)

* Tomcat 7.0.42 http://tomcat.apache.org/tomcat-7.0-doc/changelog.html

## trinidad 1.4.5 (2013-06-14)

* server should set up AJP only (if HTTP not explicitly configured) (#113)
  make sure that address/port are being set on AJP as well
* make sure environment is converted to_s + check if specified in web.xml (#112)
* config[:host] might be a string (e.g. using rackup mode)

also see all changes in 1.4.5.B1 if upgrading from a previous stable

## trinidad_jars 1.2.4 (2013-06-12)

* Tomcat 7.0.41 http://tomcat.apache.org/tomcat-7.0-doc/changelog.html

## trinidad_jars 1.2.3 (2013-04-26)

* Tomcat 7.0.39 http://tomcat.apache.org/tomcat-7.0-doc/changelog.html

## trinidad 1.4.5.B1 (2013-03-08)

* Rails 4.0 threadsafe! detection
* only configure logging err handler if STDOUT/STDERR changed from JVM default
* correctly handle .war file + expanded .war dir (double) deploys
* better web app root resolution (relative to host's app base directory)
* (always) correctly categorize deployed apps into hosts
* support for more fine grained host configuration (including default host)
* (once again) working plain-old .war deployment support
* make sure context name (once set) is not changed and is kept esp. during
  rolling reloads even if (slightly) different from configured context_name
* fail-safe (zero-downtime) support for rolling reload
  when a new version of the app fails to boot the old one is kept
* depend on latest (>= 1.2.2) jars and jruby-rack >= 1.1.13
* review extensions resolution, ignore extension (warn) when it doesn't exist
* patch DirectJDKLog to disable log source thus we won't generate a dummy
  throwable just to obtain the caller stack trace (that ain't really used)
* handle immediate log level change with JUL's LogFactory
* avoid work_dir deletion on context destory (during rolling reloads)
* do not allow public.root ('/') to be used as FS root (#93)

## trinidad_jars 1.2.2 (2013-03-08)

* Tomcat 7.0.37 http://tomcat.apache.org/tomcat-7.0-doc/changelog.html

## trinidad_jars 1.2.1 (2013-02-21)

* Tomcat 7.0.35 http://tomcat.apache.org/tomcat-7.0-doc/changelog.html

## trinidad_jars 1.2.0 (2013-01-14)

* Tomcat 7.0.33 http://tomcat.apache.org/tomcat-7.0-doc/changelog.html
* patched JULI's DirectJDKLog to not generate stack traces for every log
  by default (can be turned back using org.apache.juli.logging.logSource=true)
* native (Java) implementations for logging classes, to keep logging AFAP

## trinidad 1.4.4 (2012-10-19)

* make sure setting FileHandler's rotatable to false works (#82)
* first draft for web-app logging configuration to avoid use hacks e.g.
  for disabling rotation (this feature is not yet official and might change)
* fix new lines (ala default Rails.logger) in console output
* set correct (JRuby-Rack) layout for web apps and use app.root
  (besides rails.root)
* check whether (jruby.) params are specified in system properties (#92)
* fix JUL::ConsoleHandler - not setting up JRuby streams correctly (#88)

## trinidad_jars 1.1.1 (2012-10-17)

* Tomcat 7.0.32 http://tomcat.apache.org/tomcat-7.0-doc/changelog.html
* avoid using the "global" (and redundant) TRINIDAD_LIBS constant
* load the .jar files instead of requiring them (no more $: polution)

## trinidad 1.4.3 (2012-09-20)

* allow to keep (and configure) the jsp servlet (support for serving .jsp pages)
* decreased jruby.runtime.acquire.timeout default from 10 to 5 seconds
* removed Threads=MIN:MAX option from Rack::Handler (makes no sense right now)
* trinidad (CLI) :
  - added --runtimes option to allow specifying pool size on command-line
  - deprecated libs_dir and classes_dir added java_lib and java_classes
  - deprecated --apps option and use the same name as in config --apps_base
  - now prints some more accurate defaults that are used
* leverage Tomcat's default servlet for serving static asset files :
  - use a customized default servlet to handle the public.root assets
  - support for providing aliases (to achieve desired backward compatibility)
  - support for allowing caching and tuning (asset) cache parameters
* allow absolute paths with :default_web_xml / :web_xml configuration option
* replace :libs_dir and :classes_dir with :java_lib and :java_classes options
  (backwards compatible) with sensible defaults *lib/java* and *lib/java/classes*
* make sure application :work_dir is actually being set on the context
  and change it by default to to [RAILS_ROOT]/tmp
* web application's :web_app_dir deprecated in favor of the :root_dir option
* make sure the rack servlet loads on startup by default
* allow init_params: to be configured with (rack/default) servlet options
* allow for default (servlet) overrides using default_servlet:
* better rack_servlet: configuration
  - accepts a servlet instance (and assumes it is configured)
  - 'rack' naming convention might be used to use custom servlet class
  - support for servlet :mapping and :load_on_startup options
* fix async_supported: option not propagating correctly into servlet
* support for loading META-INF/context.xml from the (classes) class-path

Requires JRuby-Rack >= 1.1.10 and thus includes the following features :

 - Rails 3.1+ `render stream: true` support with HTTP chunked encoding
 - initial compatibility with Rails master (getting torwards 4.0.0.beta)
 - all Rack applications now support pooling runtimes (not just Rails)
 - better and more predictable application pool initialization
 - support for fully leveraging the Servlet API for parameters/cookies
   this is useful for Java integration cases when a servlet/filter consumes
   the input stream before Rails (e.g. valves such as AccessLogValve).
   it is an experimental feature that might change in the future and
   currently requires setting 'jruby.rack.handler.env' param to 'servlet'

## trinidad_jars 1.1.0 (2012-09-20)

* include java code compiled into trinidad-rb.jar for Trinidad
* otherwise same as previous version (1.0.7) uses Tomcat 7.0.30

## trinidad 1.4.2 (2012-09-19) YANKED

## trinidad_jars 1.0.7 (2012-09-12)

* Tomcat 7.0.30 http://tomcat.apache.org/tomcat-7.0-doc/changelog.html

## trinidad_jars 1.0.6 (2012-08-21)

* Tomcat 7.0.29 http://tomcat.apache.org/tomcat-7.0-doc/changelog.html

## trinidad 1.4.1 (2012-08-17)

* make sure file logging rotates correctly when file handler attempts rolling
  after midnight (#81)
* refined (backwards-compatible) extension API
  - options attr reader
  - override_tomcat? no longer needed simply return a tomcat duck
  - expose camelize + symbolize helpers
  - WebAppExtension should only get a single context argument on configure
* better rails (2.3/3.x) detection with environment.rb
* minor server updates
  - expose configured web_apps
  - add a trap? helper (for easier overrides)
  - introduce a stop! for stopping and destroying a the server

## trinidad 1.4.0 (2012-07-24)

* fix incorrect context-param parsing and only configure logging when
  deployment descriptor did not specified jruby.rack.logging param (#76)
* deep symbolize_options should account for arrays of hashes (#80)
* requires latest Tomcat 7.0.28 (jars 1.0.5) due context reloading fix
* requires latest jruby-rack 1.1.7 due delegating RackLogger to JUL
* Trinidad::WebApp API revisited some changes are non-backwards compatible !
* enable running multiple applications with different ruby versions (using the
  jruby_compat_version configuration option)
* allow arbitrary keys to be stored with Trinidad::Configuration
* changed Trinidad::Lifecycle::Base to be a (ruby-like) base lifecycle listener
  skeleton implementation, thus removed all web app specifics from the class
  (include Trinidad::Lifecycle::WebApp::Shared to gain the same functionality)
* removed unused Trinidad::Rack module and KeyTool from Trinidad::Tomcat
* reinvented server/application logging with Trinidad::Logging :
  - refactored Trinidad's global logging configuration with JUL
  - application logs into log/env.log by default with daily rolling
  - console logs are now less chatty and only print logs from applications
    running in development mode (configuration to come in a later release)
  - make sure Trinidad's custom log formatter prints thrown exceptions
  - use local timestamps with (file) log formatter by default
* Trinidad::Server#add_web_app for code re-use during rolling redeploys
* refactored application (monitor based `touch 'tmp/restart.txt'`) reloading
  - bring back synchronous context reloading and make it default
  - "zero downtime" async rolling reload is still supported and configurable via
    the reload_strategy: rolling configuration option
  - updated the context restart code - hot deploys should now work reliably (#75)
  - moved Trinidad::Lifecycle::Host under Trinidad::Lifecycle::WebApp::Host
  - Trinidad::Lifecycle::Host now accepts a server instance instead of a tomcat
  - introduced Trinidad::WebApp::Holder to be used instead of bare Hash
* add async_supported attribute for servlet (3.0) configuration

## trinidad_jars 1.0.5 (2012-07-03)

* Upgrade to Tomcat 7.0.28
* Patched org.apache.juli.FileHandler to allow daily rolling customization

## trinidad_jars 1.0.4 (2012-06-14)

* Upgrade to Tomcat 7.0.27

## trinidad_jars 1.0.3 (2012-04-04)

* Upgrade to Tomcat 7.0.26

## trinidad 1.3.5 (2012-04-04)

* Correctly detect :rackup from main config for web apps (#66)
* Rearrange trinidad.gemspec to be (always) usable with Bundler's :git paths.
* Use out/err streams from the Ruby runtime for logging instead of the default console log handler.
* Make sure tomcat exits on initialization failure.
* Yield from block passed to Trinidad's Rack::Handler

## trinidad 1.3.4 (2012-02-20)

* Do not explicitely load rack/handler/trinidad.rb, it solves load issues with trinidad_init_services.

## trinidad 1.3.3 (2012-02-16)

* Fix issues loading the default configuration file from the rack handler

## trinidad 1.3.2 (2012-01-13)

* Fix #29: Rack::Handler.register not found error

## trinidad 1.3.1 (2012-01-06)

* Fix Rack handler configuration issues

## trinidad_jars 1.0.2 (2011-12-31)

* Bump Tomcat's version to 7.0.23

## trinidad 1.3.0 (2011-12-30)

* Support for virtual hosts
* Ruby configuration DSL
* Rack handler

## trinidad 1.2.3 (2011-07-13)

* fix JRuby class loader generation with hot deploy

## trinidad 1.2.2 (2011-07-12)

* Better log formatter
* Allow to use an ERB template as configuration file
* Fix trinidad_init_services compatibility issues

## trinidad 1.2.1 (2011-06-15)

* Allow to specify the monitor file from the command line

## trinidad 1.2.0 (2011-05-24)

* Zero downtime hot deploy
* Autodetect framework and threadsafe environment
* Upgrade jruby-rack dependency to 1.0.9

## trinidad 1.1.1 (2011-03-27)

* Remove shared runtime initialization

## trinidad 1.1.0 (2011-03-18)

* Hot deployment integrated in the core gem
* Load config/trinidad.yml by default without the `-f` option if it exists
* Load config.ru for applications under the `apps_base` directory when the option is enabled
* Share the JRuby runtime with JRuby-Rack

## trinidad_jars 1.0.1 (2011-03-17)

* Upgrade to Tomcat 7.0.11

## trinidad_jars 1.0.0 (2011-01-18)

* Upgrade to Tomcat 7.0.6, first stable release of the Tomcat 7 branch.

## trinidad 1.0.5 (2011-01-13)

* Fix trailing spaces on arguments. Thank you Windows.

## trinidad 1.0.4 (2011-01-11)

* Add 'jruby.compat.version' parameter to let jruby-rack loads on 1.9 mode

## trinidad 1.0.3 (2010-12-08)

* fix TRINIDAD-31: fix bug causing trailing slashes errors loading assets

## trinidad 1.0.2 (2010-11-11)

* stop using application directory as work directory to prevent TRINIDAD-27 and other issues

## trinidad_jars 0.3.3 (2010-11-11)

* fix TRINIDAD-27: Tomcat 7.0.2 deletes working directory upon shutdown

## trinidad 1.0.1 (2010-11-04)

* fix problem loading lifecycle

## trinidad 1.0.0 (2010-11-04)

* Warbler support
* Add APR listener to run under native connectors
* fixes #24: setting address doesn't affect listening socket
* fix issues configuring the logger out of the lifecycle listener

## trinidad 0.9.12 (2010-10-21)

* fix problems loading tomcat classes from rack application
* fix xml parsing of web.xml. Thanks to Karol Bucek

## trinidad 0.9.11 (2010-10-20)

* configure applications base directory to run several applications into the same container

## trinidad 0.9.10 (2010-10-02)

* configure logging as expected by rails applications

## trinidad 0.9.9 (2010-09-29)

* set tomcat's server address properly

## trinidad 0.9.8 (2010-09-27)

* fix http connector protocol enabling nio

## trinidad 0.9.7 (2010-09-27)

* add option --adress to set the Trinidad's host

## trinidad_jars 0.3.2 (2010-09-26)

* fix TRINIDAD-21: trinidad_jars 0.3.1 breaks logging extension

## trinidad 0.9.6 (2010-09-12)

* add option to specify the application directory path from the command line

## trinidad_jars 0.3.1 (2010-09-12)

* update to Tomcat 7.0.2
* fix TRINIDAD-17: NPE when web app has a context path

## trinidad 0.9.5 (2010-08-08)

* fix TRINIDAD-15: trinidad defines String#camelize incompatibly with ActiveSupport: Argument is missing
* update JRuby-Rack dependency to avoid compatibility issues with rvm'

## trinidad 0.9.4 (2010-08-04)

* fix error configuring ssl

## trinidad 0.9.3 (2010-07-27)

* Tomcat updated to version 7.0.0
* fix TRINIDAD-9: Tomcat SSL configure options keystore and keystoreFile
* fix TRINIDAD-10: When a web.xml is provided the tomcat's context doesn't start properly

## trinidad 0.9.2 (2010-05-24)

* Autoload the rackup file when it's under the directory WEB-INF.
* Let jruby-rack reads the rackup file instead of passing its content as an init parameter.
* Allow to configure the rack servlet from the configuration options.
* Allow to use crt files to configure SSL

## trinidad 0.9.1 (2010-05-09)

* Move all configuration logic to a Lifecycle listener:
    - Keeps the initial configuration so the provided web xml files are no more needed.
    - Avoids workarounds in the hotdeploy extension.
* Disable more Tomcat's default behaviours. Process Tlds is also disabled.
* Allow to specify webapp extensions in the extensions root section.
* Allow to configure the Http connector.

## trinidad 0.9.0 (2010-04-28)

* Tomcat updated to version 6.0.26, added constant to get its version.
* Jars cleaned, 300kb less to load :)
* Default configuration file name moved from tomcat.yml to trinidad.yml
* Fixes bug merging configuration files
* Configuring application through web.xml to avoid weird lifecycle problems

## trinidad 0.8.3 (2010-04-17)

* Extensions improvements:
    - Enable command line extensions.
    - Allow to overload the server.

## trinidad 0.8.2 (2010-04-09)

* Fixes yaml parser error. Thank to @elskwid

## trinidad 0.8.1 (2010-04-06)

* Uses JRuby-Rack gem

## trinidad 0.8.0 (2010-04-04)

* Support for extensions (database connection pooling is the first one using it)
* Splitting the gem in two, the core gem and the jars gem

## trinidad 0.7.0 (2009-12-01)

* Support to run different applications within the same Tomcat container

## trinidad 0.6.0 (2009-11-02)

* Rackup compatibe frameworks support

## trinidad 0.5.0 (2009-10-27)

* JRuby-Rack updated to version 0.9.5
* Added Rack dependency to avoid using vendorized version

## trinidad 0.4.1 (2009-07-26)

* using jruby-rack development version to solve some bugs related with it.

## trinidad 0.4.0 (2009-07-16)

* support for ssl connections
* support for ajp connections

## trinidad 0.3.0 (2009-07-07)

* project renamed due to tomcat legal issues

## trinidad 0.2.0 (2009-06-23)

* custom configuration from a yaml file
* load options from a custom web.xml

## trinidad 0.1.2

* Autoload application custom jars and classes.
* Added some specs.
* Server refactor.

## trinidad 0.1

* Initial release.
* Running default rails applications.


text:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<meta http-equiv="Content-Type" content="text/xhtml;charset=UTF-8"/>
<meta http-equiv="X-UA-Compatible" content="IE=9"/>
<title>Lighthouse: lapack/chpcon.f File Reference</title>
<link href="tabs.css" rel="stylesheet" type="text/css"/>

<link href="tabs.css" rel="stylesheet" type="text/css" />
<link href="doxygen.css" rel="stylesheet" type="text/css"/>

lapack

Functions/Subroutines

chpcon.f File Reference

Functions/Subroutines

subroutine 	chpcon (UPLO, N, AP, IPIV, ANORM, RCOND, WORK, INFO)

 	CHPCON  More...

Function/Subroutine Documentation

          subroutine chpcon
          (
          character
          UPLO,

          integer
          N,

          complex, dimension( * )
          AP,

          integer, dimension( * )
          IPIV,

          real
          ANORM,

          real
          RCOND,

          complex, dimension( * )
          WORK,

          integer
          INFO

          )

CHPCON

 Download CHPCON + dependencies

 [TGZ]

 [ZIP]

 [TXT]

Purpose:

 CHPCON estimates the reciprocal of the condition number of a complex
 Hermitian packed matrix A using the factorization A = U*D*U**H or
 A = L*D*L**H computed by CHPTRF.

 An estimate is obtained for norm(inv(A)), and the reciprocal of the
 condition number is computed as RCOND = 1 / (ANORM * norm(inv(A))).

Parameters

[in]	UPLO
          UPLO is CHARACTER*1
          Specifies whether the details of the factorization are stored
          as an upper or lower triangular matrix.
          = 'U':  Upper triangular, form is A = U*D*U**H;
          = 'L':  Lower triangular, form is A = L*D*L**H.

[in]	N
          N is INTEGER
          The order of the matrix A.  N >= 0.

[in]	AP
          AP is COMPLEX array, dimension (N*(N+1)/2)
          The block diagonal matrix D and the multipliers used to
          obtain the factor U or L as computed by CHPTRF, stored as a
          packed triangular matrix.

[in]	IPIV
          IPIV is INTEGER array, dimension (N)
          Details of the interchanges and the block structure of D
          as determined by CHPTRF.

[in]	ANORM
          ANORM is REAL
          The 1-norm of the original matrix A.

[out]	RCOND
          RCOND is REAL
          The reciprocal of the condition number of the matrix A,
          computed as RCOND = 1/(ANORM * AINVNM), where AINVNM is an
          estimate of the 1-norm of inv(A) computed in this routine.

[out]	WORK
          WORK is COMPLEX array, dimension (2*N)

[out]	INFO
          INFO is INTEGER
          = 0:  successful exit
          < 0:  if INFO = -i, the i-th argument had an illegal value

Author

Univ. of Tennessee

Univ. of California Berkeley

Univ. of Colorado Denver

NAG Ltd.

Date

November 2011

<address class="footer">
Generated on Wed Apr 1 2015 16:27:43 for Lighthouse by

 1.8.9.1
</address>


text:
At the moment there seems to only be two type of mobile apps...

Native and HTML...

Most business tend to try native... and them move to HTML to save cost on dev and testing (which in the long run doesn't work).
I decided that there must be a middle ground, and as i have not heard of it, i decided to create it my self as a POC.

This POC is more of a way of satisfying my curiosity.

The idea is to use rest service to describe the layout of the views, to return the data for each view and key resources that are needed.

layout = common view models that can be built (like lego) block with each other that describe layouts, these view models will them be translated into HTML/JS (web), UIView/UIViewController (iOS), Activities/Fragments (Android).

data = json data objects

key resources = images, text (cached on each platform when approperite)

The end idea is to create a eco system of common building blocks that can be used to quickly create new flows that have a consistent look and feel.

This project will consist of:
 - DNA.RestServices
 - DNA.Web
 - DNA.Android
 - DNA.iOS


text:
<?php
// Exit if the file is accessed directly over web
if ( ! defined( 'ABSPATH' ) ) {
	exit;
}
/**
 * For example
 * Here is the text field rendering
 */
class MPP_Admin_Settings_Field_Text extends MPP_Admin_Settings_Field {

    public function __construct( $field ) {

        parent::__construct( $field );
    }

    public function render( $args ) {

        parent::render( $args );
    }
}


text:
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
          package="camp.kuznetsov.rn.yandexmapkit">

</manifest>


text:
package gov.va.hmp.healthtime.jackson;

import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.ser.std.StdSerializer;
import gov.va.hmp.healthtime.IntervalOfTime;
import gov.va.hmp.healthtime.format.IntervalOfTimeFormat;

import java.io.IOException;

public class IntervalOfTimeSerializer extends StdSerializer<IntervalOfTime> {

    public IntervalOfTimeSerializer() {
        super(IntervalOfTime.class);
    }

    @Override
    public void serialize(IntervalOfTime ivl, JsonGenerator jgen, SerializerProvider provider) throws IOException, JsonProcessingException {
        if (provider.isEnabled(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS)) {
            // Timestamp here actually means an array of values; for an interval of time we write out two arrays
            jgen.writeStartObject();
            jgen.writeFieldName("low");
//            jgen.writeArrayFieldStart("low");
            jgen.writeObject(ivl.getLow());
            jgen.writeFieldName("high");
//            jgen.writeArrayFieldStart("high");
            jgen.writeObject(ivl.getHigh());
            jgen.writeEndObject();

//            jgen.writeStartArray();
//            jgen.writeNumber(t.getYear());
//            if (t.isMonthSet())
//                jgen.writeNumber(t.getMonth());
//            if (t.isDateSet())
//                jgen.writeNumber(t.getDate());
//            if (t.isHourSet())
//                jgen.writeNumber(t.getHour());
//            if (t.isMinuteSet())
//                jgen.writeNumber(t.getMinute());
//            if (t.isSecondSet())
//                jgen.writeNumber(t.getSecond());
//            if (t.isMillisecondSet())
//                jgen.writeNumber(t.getMillisecond());
//            jgen.writeEndArray();
        } else {
            jgen.writeString(IntervalOfTimeFormat.print(ivl));
        }
    }
}


text:
using System;
using System.Collections.Generic;
using System.Xml.Serialization;
using System.Text;
using System.IO;

using Amazon.Runtime;
using Amazon.Runtime.Internal;

namespace Amazon.Redshift.Model
{
    /// <summary>
    /// Container for the parameters to the ModifyClusterSubnetGroup operation.
    /// <para> Modifies a cluster subnet group to include the specified list of VPC subnets. The operation replaces the existing list of subnets
    /// with the new list of subnets. </para>
    /// </summary>
    /// <seealso cref="Amazon.Redshift.AmazonRedshift.ModifyClusterSubnetGroup"/>
    public class ModifyClusterSubnetGroupRequest : AmazonWebServiceRequest
    {
        private string clusterSubnetGroupName;
        private string description;
        private List<string> subnetIds = new List<string>();

        /// <summary>
        /// The name of the subnet group to be modified.
        ///
        /// </summary>
        public string ClusterSubnetGroupName
        {
            get { return this.clusterSubnetGroupName; }
            set { this.clusterSubnetGroupName = value; }
        }

        /// <summary>
        /// Sets the ClusterSubnetGroupName property
        /// </summary>
        /// <param name="clusterSubnetGroupName">The value to set for the ClusterSubnetGroupName property </param>
        /// <returns>this instance</returns>
        [Obsolete("The With methods are obsolete and will be removed in version 2 of the AWS SDK for .NET. See http://aws.amazon.com/sdkfornet/#version2 for more information.")]
        public ModifyClusterSubnetGroupRequest WithClusterSubnetGroupName(string clusterSubnetGroupName)
        {
            this.clusterSubnetGroupName = clusterSubnetGroupName;
            return this;
        }

        // Check to see if ClusterSubnetGroupName property is set
        internal bool IsSetClusterSubnetGroupName()
        {
            return this.clusterSubnetGroupName != null;
        }

        /// <summary>
        /// A text description of the subnet group to be modified.
        ///
        /// </summary>
        public string Description
        {
            get { return this.description; }
            set { this.description = value; }
        }

        /// <summary>
        /// Sets the Description property
        /// </summary>
        /// <param name="description">The value to set for the Description property </param>
        /// <returns>this instance</returns>
        [Obsolete("The With methods are obsolete and will be removed in version 2 of the AWS SDK for .NET. See http://aws.amazon.com/sdkfornet/#version2 for more information.")]
        public ModifyClusterSubnetGroupRequest WithDescription(string description)
        {
            this.description = description;
            return this;
        }

        // Check to see if Description property is set
        internal bool IsSetDescription()
        {
            return this.description != null;
        }

        /// <summary>
        /// An array of VPC subnet IDs. A maximum of 20 subnets can be modified in a single request.
        ///
        /// </summary>
        public List<string> SubnetIds
        {
            get { return this.subnetIds; }
            set { this.subnetIds = value; }
        }
        /// <summary>
        /// Adds elements to the SubnetIds collection
        /// </summary>
        /// <param name="subnetIds">The values to add to the SubnetIds collection </param>
        /// <returns>this instance</returns>
        [Obsolete("The With methods are obsolete and will be removed in version 2 of the AWS SDK for .NET. See http://aws.amazon.com/sdkfornet/#version2 for more information.")]
        public ModifyClusterSubnetGroupRequest WithSubnetIds(params string[] subnetIds)
        {
            foreach (string element in subnetIds)
            {
                this.subnetIds.Add(element);
            }

            return this;
        }

        /// <summary>
        /// Adds elements to the SubnetIds collection
        /// </summary>
        /// <param name="subnetIds">The values to add to the SubnetIds collection </param>
        /// <returns>this instance</returns>
        [Obsolete("The With methods are obsolete and will be removed in version 2 of the AWS SDK for .NET. See http://aws.amazon.com/sdkfornet/#version2 for more information.")]
        public ModifyClusterSubnetGroupRequest WithSubnetIds(IEnumerable<string> subnetIds)
        {
            foreach (string element in subnetIds)
            {
                this.subnetIds.Add(element);
            }

            return this;
        }

        // Check to see if SubnetIds property is set
        internal bool IsSetSubnetIds()
        {
            return this.subnetIds.Count > 0;
        }
    }
}


text:
package edu.sampleu.admin;

import org.kuali.rice.testtools.common.JiraAwareFailable;
import org.kuali.rice.testtools.selenium.AutomatedFunctionalTestUtils;
import org.kuali.rice.testtools.selenium.WebDriverUtils;

/**
 * @author Kuali Rice Team (rice.collab@kuali.org)
 */
public abstract class ConfigParameterTypeLookUpAndInquireAftBase extends AdminTmplMthdAftNavBase {

    /**
     * ITUtil.PORTAL+"?channelTitle=Parameter&channelUrl="+WebDriverUtils.getBaseUrlString()+
     * "/kr/lookup.do?methodToCall=start&businessObjectClassName=org.kuali.rice.coreservice.impl.parameter.ParameterBo&docFormKey=88888888&returnLocation="
     * +ITUtil.PORTAL_URL+ ITUtil.HIDE_RETURN_LINK;
     */
    public static final String BOOKMARK_URL = AutomatedFunctionalTestUtils.PORTAL+"?channelTitle=Parameter%20Type&channelUrl="+ WebDriverUtils
            .getBaseUrlString()+"/kr/lookup.do?methodToCall=start&businessObjectClassName=org.kuali.rice.coreservice.impl.parameter.ParameterTypeBo&docFormKey=88888888&returnLocation="+
            AutomatedFunctionalTestUtils.PORTAL_URL+ AutomatedFunctionalTestUtils.HIDE_RETURN_LINK;

    @Override
    protected String getBookmarkUrl() {
        return BOOKMARK_URL;
    }

    /**
     * {@inheritDoc}
     * Parameter
     * @return
     */
    @Override
    protected String getLinkLocator() {
        return "Parameter Type";
    }

    public void testConfigParameterTypeLookUpAndInquireBookmark(JiraAwareFailable failable) throws Exception {
        testConfigParameterTypeLookUpAndInquire();
        passed();
    }

    public void testConfigParameterTypeLookUpAndInquireNav(JiraAwareFailable failable) throws Exception {
        testConfigParameterTypeLookUpAndInquire();
        passed();
    }

    public void testConfigParameterTypeLookUpAndInquire() throws Exception
    {
        selectFrameIframePortlet();
        waitAndClickSearchSecond();
        waitAndClickByLinkText("Authorization");
        switchToWindow("Kuali :: Inquiry");
        selectFrameIframePortlet();
        waitAndClick("input.globalbuttons");
    }
}


text:
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">

    <title>Communication</title>
    <meta name="description" content="">

    <meta name="HandheldFriendly" content="True">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">

    <link rel="shortcut icon" href="../favicon.ico">

    <link rel="stylesheet" type="text/css" href="http://fonts.googleapis.com/css?family=Merriweather:300,700,700italic,300italic%7COpen+Sans:700,400">
    <link rel="stylesheet" type="text/css" href="../assets/css/screen.css?v=36acbe3795">

    <link rel="canonical" href="index.html">
    <meta name="referrer" content="no-referrer-when-downgrade">
    <link rel="amphtml" href="amp/index.html">

    <meta property="og:site_name" content="Pat Kayongo's Software Development Blog">
    <meta property="og:type" content="article">
    <meta property="og:title" content="Communication">
    <meta property="og:description" content="A few years ago, my goal was to be the best software developer ever. Over time, that didn't go away, but I realised that best is an almost impossible tasks, as there are many different kinds of good. My next best goal was to become a really good software">
    <meta property="og:url" content="http://localhost:2368/communication/">
    <meta property="article:published_time" content="2014-08-14T11:19:06.000Z">
    <meta property="article:modified_time" content="2014-08-14T11:19:06.000Z">
    <meta property="article:tag" content="architecture">
    <meta property="article:tag" content="communication">
    <meta property="article:tag" content="design">
    <meta property="article:tag" content="development">
    <meta property="article:tag" content="software">

    <meta name="twitter:card" content="summary">
    <meta name="twitter:title" content="Communication">
    <meta name="twitter:description" content="A few years ago, my goal was to be the best software developer ever. Over time, that didn't go away, but I realised that best is an almost impossible tasks, as there are many different kinds of good. My next best goal was to become a really good software">
    <meta name="twitter:url" content="http://localhost:2368/communication/">
    <meta name="twitter:label1" content="Written by">
    <meta name="twitter:data1" content="Patrick Kayongo">
    <meta name="twitter:label2" content="Filed under">
    <meta name="twitter:data2" content="architecture, communication, design, development, software">
    <meta name="twitter:site" content="@patkayongo">

    <meta name="generator" content="Ghost 0.11">
    <link rel="alternate" type="application/rss+xml" title="Pat Kayongo's Software Development Blog" href="../rss/index.html">

Menu

        Close

Home

        Subscribe

            Menu

Communication

                <time class="post-date" datetime="2014-08-14">14 August 2014</time>  on architecture, communication, design, development, software

A few years ago, my goal was to be the best software developer ever. Over time, that didn't go away, but I realised that best is an almost impossible tasks, as there are many different kinds of good.

My next best goal was to become a really good software architect. I have been focusing a lot more on good software design, and less on technical details of a language. And I'm slowly becoming good at it.

I sometimes look at the way other people do things, and wonder "Can they not see that this is not the 'right' way to do this?". When topics such as Test Driven Development, and other design 'best practices' come up in discussion, I am that guy who is wholly for it, yet not everyone agrees. Actually, some don't even understand what my fuss may be about. Software is meant to work, and what happens under the bonnet can be seen as irrelevant to the end user. I can throw terms around like 'technical debt', but unless someone experienced the effects of technical debt, even this doesn't quite work out.

I am fully convinced of the negative effects of bad software design. I am also fully convinced of the benefits of good software design. But not everyone else is. There are other clearer voices shouting out loud, such as the increased cost of added time writing tests, and the perceived increased scope. And the best way for the voice of good design to be heard is for it to be clear and understandable.

A good software developer isn't only someone who is technically proficient. A good software developer is also one who can make what he understands clear to those who do not understand. If, for example, a building falls down because of a faulty foundation, and one of the artisans failed to effectively communicate risk, the fault will not be on those who failed to understand, but on the one who failed to communicate.

My communication skills aren't the best at the moment. But the mastery of this skill is a goal I've set for myself, as part of becoming an amazing developer.

How is your communication?

                Patrick Kayongo's Picture

Patrick Kayongo

Read more posts by this author.

Share this post

                    Twitter

                    Facebook

                    Google+

Early Thoughts on iOS Development

Recently, I started developing iOS applications for work. It's an interesting experience, especially because I once vowed never…

Why Microsoft #1

I was never into movies for a large part of my life. The idea of sitting in front of…

Pat Kayongo's Software Development Blog © 2017

Proudly published with Ghost


text:
use strict;
use warnings;
use Bio::SeqIO;

my $ArgN = @ARGV;
my $usage = "\nUSAGE:
	perl <scriptname.pl> <FastaFile> <File size in Mo>

	This script will rewrite a fasta file (typically, a genome) in pieces of X Mo (~correlated to the total length of sequences)

	<FastaFile>       --> (STRING)  Input file in fasta format (typically, a genome)
	<File size in Mo> --> (INTEGER) To set X, the size of output files in Mo

	NB: - if cut results in more than 100 files, you should then replace \"%02d\" by \"%03d\" at line 39.
	    - if some sequences are longer than the set size in Mo, then files containing them will be bigger anyway since the script won't cut a sequence.\n\n";

die $usage if ($ArgN < 2);

my $file = shift;
my $len = shift;
$len = $len*1000000; #convert Mo in nucleotide size (approx)
my $fasta = Bio::SeqIO->new(-file => $file, -format => "fasta") or die "Failed to create SeqIO object from $file $!\n";

mkdir "$file.Cut"; #no behavior if output dir exists -> user will need to fix any issue regarding that.
my $fileo = $1 if ($file =~ /^(.*)\.fa/);

my $i = 1;
my $seqnb = 0;
my $currlen = 0;
while( my $sequence = $fasta->next_seq() ) {
	my $outname = "$file.Cut/$fileo.".sprintf("%02d", $i).".fa";
	my $out = Bio::SeqIO->new(-file => ">>$outname", -format => "fasta") or die "Failed to create outputfile $i $outname $!\n";
	$out->write_seq($sequence);
	$currlen += $sequence->length;
	$seqnb++;
	if ($currlen >= $len ) {
		$i++;
		$currlen = 0;
	}
}
print STDERR "\n --- DONE: number of sequences written in total = $seqnb, in $i files.\n\n";
exit;


text:
import { async, ComponentFixture, TestBed } from '@angular/core/testing';

import { PublicProfileComponent } from './public-profile.component';

describe('PublicProfileComponent', () => {
  let component: PublicProfileComponent;
  let fixture: ComponentFixture<PublicProfileComponent>;

  beforeEach(async(() => {
    TestBed.configureTestingModule({
      declarations: [ PublicProfileComponent ]
    })
    .compileComponents();
  }));

  beforeEach(() => {
    fixture = TestBed.createComponent(PublicProfileComponent);
    component = fixture.componentInstance;
    fixture.detectChanges();
  });

  it('should create', () => {
    expect(component).toBeTruthy();
  });
});


text:
//
// Markets Object
// BitGo accessor to Bitcoin market data.
//
// Copyright 2015, BitGo, Inc.  All Rights Reserved.
//

var common = require('./common');
var Q = require('q');

//
// Constructor
//
var Markets = function(bitgo) {
  this.bitgo = bitgo;
};

/**
 * Get the latest bitcoin price data
 * @param params {}
 * @param callback
 * @returns {*} an object containing price and volume data from the
 * current day in a number of currencies
 **/
Markets.prototype.latest = function(params, callback) {
  params = params || {};
  common.validateParams(params, [], [], callback);

  return this.bitgo.get(this.bitgo.url('/market/latest'))
  .result()
  .nodeify(callback);
};

/**
 * Get yesterday's bitcoin price data
 * @param params {}
 * @param callback
 * @returns {*} an object containing price and volume data from the
 * previous day in a number of currencies
 */
Markets.prototype.yesterday = function(params, callback) {
  params = params || {};
  common.validateParams(params, [], [], callback);

  return this.bitgo.get(this.bitgo.url('/market/yesterday'))
  .result()
  .nodeify(callback);
};

/**
 * Get bitcoin price data from up to 90 days prior to today
 * @param params { currencyName: the code for the desired currency, for example USD }
 * @param callback
 * @returns {*} an object containing average prices from a number of previous days
 */
Markets.prototype.lastDays = function(params, callback) {
  params = params || {};
  common.validateParams(params, ['currencyName'], [], callback);

  var days = !isNaN(parseInt(params.days)) ? parseInt(params.days) : 90;

  if (days && days < 0) {
    throw new Error('must use a non-negative number of days');
  }

  return this.bitgo.get(this.bitgo.url('/market/last/' + days + '/' + params.currencyName))
  .result()
  .nodeify(callback);
};

module.exports = Markets;


text:
package model

import (
	"errors"

	"golang.org/x/net/context"
	"google.golang.org/appengine/datastore"
)

// RobotTestResultsKind is the kind used in Datastore to store entities RobotTestResults entities.
const RobotTestResultsKind = "RobotTestResults"

// RobotTestResultss is just a slice of RobotTestResults.
type RobotTestResultss []RobotTestResults

// KeyedRobotTestResults is a struct that embeds RobotTestResults and also contains a Key, mainly used for encoding to JSON.
type KeyedRobotTestResults struct {
	*RobotTestResults
	Key *datastore.Key
}

// Key is a shorthand to fill a KeyedRobotTestResults with an entity and it's key.
func (ƨ *RobotTestResults) Key(key *datastore.Key) *KeyedRobotTestResults {
	return &KeyedRobotTestResults{
		RobotTestResults: ƨ,
		Key:              key,
	}
}

// Key is a shorthand to fill a slice of KeyedRobotTestResults with some entities alongside their keys.
func (ƨ RobotTestResultss) Key(keys []*datastore.Key) (keyed []KeyedRobotTestResults) {
	if len(keys) != len(ƨ) {
		panic("Key() called on an slice with len(keys) != len(slice)")
	}

	keyed = make([]KeyedRobotTestResults, len(ƨ))
	for i := range keyed {
		keyed[i] = KeyedRobotTestResults{
			RobotTestResults: &ƨ[i],
			Key:              keys[i],
		}
	}
	return
}

// Put will put this RobotTestResults into Datastore using the given key.
func (ƨ RobotTestResults) Put(ctx context.Context, key *datastore.Key) (*datastore.Key, error) {
	if key != nil {
		return datastore.Put(ctx, key, &ƨ)
	}

	return datastore.Put(ctx, datastore.NewIncompleteKey(ctx, "RobotTestResults", nil), &ƨ)
}

// PutWithParent can be used to save this RobotTestResults as child of another
// entity.
// This will error if parent == nil.
func (ƨ RobotTestResults) PutWithParent(ctx context.Context, parent *datastore.Key) (*datastore.Key, error) {
	if parent == nil {
		return nil, errors.New("parent key is nil, expected a valid key")
	}
	return datastore.Put(ctx, datastore.NewIncompleteKey(ctx, "RobotTestResults", parent), &ƨ)
}

// NewQueryForRobotTestResults prepares a datastore.Query that can be
// used to query entities of type RobotTestResults.
func NewQueryForRobotTestResults() *datastore.Query {
	return datastore.NewQuery("RobotTestResults")
}


text:
using System;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using UISleuth.Diagnostics;
using UISleuth.Messages;
using WebSocketSharp;
using WebSocketSharp.Server;

namespace UISleuth.Networking
{
    internal sealed class DefaultInspectorSocket : InspectorSocket
    {
        private WebSocket _client;
        private WebSocketServer _server;
        private short _port;

        private readonly ManualResetEventSlim _cancellation = new ManualResetEventSlim();
        private readonly object _lock = new object();

        public override bool IsListening => _server?.IsListening ?? false;
        public override string ConnectedClient => ConnectionQueue.Values.FirstOrDefault();
        public override bool IsClientConnected => !string.IsNullOrWhiteSpace(ConnectedClient);

        public const string Localhost = "localhost";

        public override void Listen(short port = ServiceEndpoint.MobilePort)
        {
            lock (_lock)
            {
                _port = port;

                _server = new WebSocketServer(_port, false)
                {
                    KeepClean = false,
                    WaitTime = ServiceEndpoint.PingWaitTime
                };

                _server.AddWebSocketService(ServiceEndpoint.MobilePath, () => new InspectorServerBehavior(this));
                _server.AddWebSocketService(ServiceEndpoint.MobileHeartbeatPath, () => new HeartbeatServerBehavior(_server));
                _cancellation.Reset();
            }

            Task.Run(() =>
            {
                if (_server != null)
                {
                    lock (_lock)
                    {
                        _server?.Start();
                    }

                    OnSocketStarted();
                }

                _cancellation.Wait();
                OnTrace(UILogLevel.Info, "Cancellation event received; stopping server.");
            });
        }

        public override void Connect(string uri)
        {
            lock (_lock)
            {
                _client = new WebSocket(uri);
            }

            Task.Run(() =>
            {
                if (_client != null)
                {
                    lock (_lock)
                    {
                        _client.OnOpen += OnClientOpen;
                        _client.OnClose += OnClientClose;
                        _client.OnError += OnClientError;
                        _client.OnMessage += OnClientMessage;
                        _client.Connect();
                    }

                    IsConnectedToRemoteServer = true;
                    OnSocketStarted();
                }

                _cancellation.Wait();
                OnTrace(UILogLevel.Info, "Cancellation event received; disconnected from remote server.");
            });
        }

        public override void Stop()
        {
            try
            {
                lock (_lock)
                {
                    if (!_cancellation.IsSet)
                    {
                        _cancellation.Set();

                        if (IsListening)
                        {
                            var reason = new ServerStoppedOk().ToJson();
                            _server.Stop(CloseStatusCode.Normal, reason);
                            _server = null;
                        }

                        if (IsConnectedToRemoteServer)
                        {
                            _client.Close(CloseStatusCode.Normal);
                            _client.OnClose -= OnClientClose;
                            _client.OnOpen -= OnClientOpen;
                            _client.OnError -= OnClientError;
                            _client.OnMessage -= OnClientMessage;

                            _client = null;
                            IsConnectedToRemoteServer = false;
                        }
                    }
                }
            }
            catch (Exception e)
            {
                OnTrace(UILogLevel.Error, $"An error occurred while stopping the socket:\n{e}");
            }
        }

        public override void Send(string message)
        {
            try
            {
                if (IsListening)
                {
                    _server.WebSocketServices.Broadcast(message);
                }

                if (IsConnectedToRemoteServer)
                {
                    _client.Send(message);
                }
            }
            catch (Exception e)
            {
                OnTrace(UILogLevel.Error, $"An error occurred while sending:\n{message}\n{e}");
            }
        }

        #region Events

        internal override void OnClientDisconnected(SocketClosedEventArgs e)
        {
            if (ConnectionQueue.ContainsKey(e.UniqueId))
            {
                ConnectionQueue.Remove(e.UniqueId);
            }

            base.OnClientDisconnected(e);
        }

        internal override void OnClientConnected(SocketConnectedEventArgs e)
        {
            if (!ConnectionQueue.ContainsKey(e.UniqueId))
            {
                ConnectionQueue.Add(e.UniqueId, e.Address);
            }

            base.OnClientConnected(e);
        }

        private void OnClientError(object sender, ErrorEventArgs e)
        {
            var ea = new SocketErrorEventArgs
            {
                Message = e.Message
            };

            OnError(ea);
        }

        private void OnClientClose(object sender, CloseEventArgs e)
        {
            var level = UILogLevel.Info;

            if (!e.WasClean)
            {
                level = UILogLevel.Error;
            }

            var ea = new SocketClosedEventArgs
            {
                Address = Localhost,
                UniqueId = Localhost
            };

            OnClientDisconnected(ea);
            OnTrace(level, $"Disconnected from remote server.{Environment.NewLine}Code: {e.Code}. Reason: {e.Reason}");

        }

        private void OnClientOpen(object sender, EventArgs e)
        {
            var ea = new SocketConnectedEventArgs
            {
                Address = Localhost,
                UniqueId = Localhost
            };

            OnClientConnected(ea);
            OnTrace(UILogLevel.Info, "Connected to remote server.");
        }

        private bool _sent = false;
        private void OnClientMessage(object sender, MessageEventArgs e)
        {
            if (e.IsText)
            {
                var msgEvent = new SocketMessageReceivedEventArgs
                {
                    Message = e.Data
                };

                if (!_sent)
                {
                    OnClientConnected(new SocketConnectedEventArgs { Address = Localhost, UniqueId = Localhost });
                    _sent = true;
                }

                OnMessage(msgEvent);
            }
        }

        #endregion
    }
}


text:
package com.guoxiaoxing.cloud.music.model;

/**
 * Created by wm on 2016/4/15.
 */
public class RadioNetInfo {

    /**
     * channelid : 11373552
     * itemid : 13015515
     * album_id : 13015216
     * title : 我从不怕爱错,就怕没爱过
     * pic : http://a.hiphotos.baidu.com/ting/pic/item/d8f9d72a6059252dcbcd03a8339b033b5ab5b947.jpg
     * type : lebo
     * desc : 音乐
     */

    private String channelid;
    private String itemid;
    private String album_id;
    private String title;
    private String pic;
    private String type;
    private String desc;

    public String getChannelid() {
        return channelid;
    }

    public void setChannelid(String channelid) {
        this.channelid = channelid;
    }

    public String getItemid() {
        return itemid;
    }

    public void setItemid(String itemid) {
        this.itemid = itemid;
    }

    public String getAlbum_id() {
        return album_id;
    }

    public void setAlbum_id(String album_id) {
        this.album_id = album_id;
    }

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public String getPic() {
        return pic;
    }

    public void setPic(String pic) {
        this.pic = pic;
    }

    public String getType() {
        return type;
    }

    public void setType(String type) {
        this.type = type;
    }

    public String getDesc() {
        return desc;
    }

    public void setDesc(String desc) {
        this.desc = desc;
    }
}


text:
JSY_ERROR_S JsyGOpen(JSYGHandle * handle) {
    JSY_ERROR_S result = JSY_SUCCEED;

    glClearColor(0.0f, 0.0f, 0.0f, 0.0f);			// Background Color
    glClearDepth(1.0f);											// Depth Buffer Setup
    glDepthFunc(GL_LEQUAL);										// Type Of Depth Testing
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);			// Enable Alpha Blending
    glEnable(GL_BLEND);											// Enable Blending
    glEnable(GL_TEXTURE_2D);									// Enable Texture Mapping
    glEnable(GL_CULL_FACE);										// Remove Back Face

    handle = nullptr;
    return result;
}

JSY_ERROR_S JsyGClose(JSYGHandle handle) {
    JSY_ERROR_S result = JSY_SUCCEED;

    return result;
}

JSY_ERROR_S JsyGDrawCircle(JSYGHandle handle) {
    JSY_ERROR_S result = JSY_SUCCEED;

    return result;
}

JSY_ERROR_S JsyGDrawLine(JSYGHandle handle) {
    JSY_ERROR_S result = JSY_SUCCEED;

    return result;
}

JSY_ERROR_S JsyGDrawSprite(JSYGHandle handle) {
    JSY_ERROR_S result = JSY_SUCCEED;

    return result;
}


text:
<?php

declare(strict_types=1);

namespace Sylius\Bundle\ShippingBundle\DependencyInjection;

use Sylius\Bundle\ResourceBundle\Controller\ResourceController;
use Sylius\Bundle\ResourceBundle\SyliusResourceBundle;
use Sylius\Bundle\ShippingBundle\Form\Type\ShipmentType;
use Sylius\Bundle\ShippingBundle\Form\Type\ShippingCategoryType;
use Sylius\Bundle\ShippingBundle\Form\Type\ShippingMethodTranslationType;
use Sylius\Bundle\ShippingBundle\Form\Type\ShippingMethodType;
use Sylius\Component\Resource\Factory\Factory;
use Sylius\Component\Resource\Factory\TranslatableFactory;
use Sylius\Component\Shipping\Model\Shipment;
use Sylius\Component\Shipping\Model\ShipmentInterface;
use Sylius\Component\Shipping\Model\ShipmentUnit;
use Sylius\Component\Shipping\Model\ShipmentUnitInterface;
use Sylius\Component\Shipping\Model\ShippingCategory;
use Sylius\Component\Shipping\Model\ShippingCategoryInterface;
use Sylius\Component\Shipping\Model\ShippingMethod;
use Sylius\Component\Shipping\Model\ShippingMethodInterface;
use Sylius\Component\Shipping\Model\ShippingMethodTranslation;
use Sylius\Component\Shipping\Model\ShippingMethodTranslationInterface;
use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition;
use Symfony\Component\Config\Definition\Builder\TreeBuilder;
use Symfony\Component\Config\Definition\ConfigurationInterface;

final class Configuration implements ConfigurationInterface
{
    /**
     * {@inheritdoc}
     */
    public function getConfigTreeBuilder(): TreeBuilder
    {
        $treeBuilder = new TreeBuilder('sylius_shipping');
        /** @var ArrayNodeDefinition $rootNode */
        $rootNode = $treeBuilder->getRootNode();

        $rootNode
            ->addDefaultsIfNotSet()
            ->children()
                ->scalarNode('driver')->defaultValue(SyliusResourceBundle::DRIVER_DOCTRINE_ORM)->end()
            ->end()
        ;

        $this->addResourcesSection($rootNode);

        return $treeBuilder;
    }

    private function addResourcesSection(ArrayNodeDefinition $node): void
    {
        $node
            ->children()
                ->arrayNode('resources')
                    ->addDefaultsIfNotSet()
                    ->children()
                        ->arrayNode('shipment')
                            ->addDefaultsIfNotSet()
                            ->children()
                                ->variableNode('options')->end()
                                ->arrayNode('classes')
                                    ->addDefaultsIfNotSet()
                                    ->children()
                                        ->scalarNode('model')->defaultValue(Shipment::class)->cannotBeEmpty()->end()
                                        ->scalarNode('interface')->defaultValue(ShipmentInterface::class)->cannotBeEmpty()->end()
                                        ->scalarNode('controller')->defaultValue(ResourceController::class)->cannotBeEmpty()->end()
                                        ->scalarNode('repository')->cannotBeEmpty()->end()
                                        ->scalarNode('factory')->defaultValue(Factory::class)->end()
                                        ->scalarNode('form')->defaultValue(ShipmentType::class)->cannotBeEmpty()->end()
                                    ->end()
                                ->end()
                            ->end()
                        ->end()
                        ->arrayNode('shipment_unit')
                            ->addDefaultsIfNotSet()
                            ->children()
                                ->variableNode('options')->end()
                                ->arrayNode('classes')
                                    ->addDefaultsIfNotSet()
                                    ->children()
                                        ->scalarNode('model')->defaultValue(ShipmentUnit::class)->cannotBeEmpty()->end()
                                        ->scalarNode('interface')->defaultValue(ShipmentUnitInterface::class)->cannotBeEmpty()->end()
                                        ->scalarNode('controller')->defaultValue(ResourceController::class)->cannotBeEmpty()->end()
                                        ->scalarNode('repository')->cannotBeEmpty()->end()
                                        ->scalarNode('factory')->defaultValue(Factory::class)->end()
                                    ->end()
                                ->end()
                            ->end()
                        ->end()
                        ->arrayNode('shipping_method')
                            ->addDefaultsIfNotSet()
                            ->children()
                                ->variableNode('options')->end()
                                ->arrayNode('classes')
                                    ->addDefaultsIfNotSet()
                                    ->children()
                                        ->scalarNode('model')->defaultValue(ShippingMethod::class)->cannotBeEmpty()->end()
                                        ->scalarNode('interface')->defaultValue(ShippingMethodInterface::class)->cannotBeEmpty()->end()
                                        ->scalarNode('controller')->defaultValue(ResourceController::class)->cannotBeEmpty()->end()
                                        ->scalarNode('repository')->cannotBeEmpty()->end()
                                        ->scalarNode('factory')->defaultValue(TranslatableFactory::class)->end()
                                        ->scalarNode('form')->defaultValue(ShippingMethodType::class)->cannotBeEmpty()->end()
                                    ->end()
                                ->end()
                                ->arrayNode('translation')
                                    ->addDefaultsIfNotSet()
                                    ->children()
                                        ->variableNode('options')->end()
                                        ->arrayNode('classes')
                                            ->addDefaultsIfNotSet()
                                            ->children()
                                                ->scalarNode('model')->defaultValue(ShippingMethodTranslation::class)->cannotBeEmpty()->end()
                                                ->scalarNode('interface')->defaultValue(ShippingMethodTranslationInterface::class)->cannotBeEmpty()->end()
                                                ->scalarNode('controller')->defaultValue(ResourceController::class)->cannotBeEmpty()->end()
                                                ->scalarNode('repository')->cannotBeEmpty()->end()
                                                ->scalarNode('factory')->defaultValue(Factory::class)->end()
                                                ->scalarNode('form')->defaultValue(ShippingMethodTranslationType::class)->cannotBeEmpty()->end()
                                            ->end()
                                        ->end()
                                    ->end()
                                ->end()
                            ->end()
                        ->end()
                        ->arrayNode('shipping_category')
                            ->addDefaultsIfNotSet()
                            ->children()
                                ->variableNode('options')->end()
                                ->arrayNode('classes')
                                    ->addDefaultsIfNotSet()
                                    ->children()
                                        ->scalarNode('model')->defaultValue(ShippingCategory::class)->cannotBeEmpty()->end()
                                        ->scalarNode('interface')->defaultValue(ShippingCategoryInterface::class)->cannotBeEmpty()->end()
                                        ->scalarNode('controller')->defaultValue(ResourceController::class)->cannotBeEmpty()->end()
                                        ->scalarNode('repository')->cannotBeEmpty()->end()
                                        ->scalarNode('factory')->defaultValue(Factory::class)->end()
                                        ->scalarNode('form')->defaultValue(ShippingCategoryType::class)->cannotBeEmpty()->end()
                                    ->end()
                                ->end()
                            ->end()
                        ->end()
                    ->end()
                ->end()
            ->end()
        ;
    }
}