/**
* Copyright (C) 2011 Brian Ferris <bdferris@onebusaway.org>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.onebusaway.users.impl;
import java.util.List;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.atomic.AtomicInteger;
import org.onebusaway.users.model.User;
import org.onebusaway.users.model.UserProperties;
import org.onebusaway.users.services.UserDao;
import org.onebusaway.users.services.UserPropertiesMigration;
import org.onebusaway.users.services.UserPropertiesMigrationStatus;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class UserPropertiesMigrationBulkOperation<T extends UserProperties> {
private static final int FETCH_LIMIT = 100;
private static Logger _log = LoggerFactory.getLogger(UserPropertiesMigrationBulkOperation.class);
private ScheduledExecutorService _executor = Executors.newSingleThreadScheduledExecutor();
private UserPropertiesMigration _propertiesMigration;
private Class<T> _target;
private UserDao _dao;
private int _numberOfUsers;
private AtomicInteger _numberOfUsersProcessed = new AtomicInteger();
private AtomicInteger _numberOfUsersConverted = new AtomicInteger();
private boolean _canceled = false;
public static <T extends UserProperties> UserPropertiesMigrationBulkOperation<T> execute(
UserDao dao, UserPropertiesMigration propertiesMigration, Class<T> target) {
UserPropertiesMigrationBulkOperation<T> op = new UserPropertiesMigrationBulkOperation<T>(
dao, propertiesMigration, target);
op.run();
return op;
}
private UserPropertiesMigrationBulkOperation(UserDao dao,
UserPropertiesMigration propertiesMigration, Class<T> target) {
_dao = dao;
_propertiesMigration = propertiesMigration;
_target = target;
}
public void run() {
_numberOfUsers = _dao.getNumberOfUsers();
for (int i = 0; i < _numberOfUsers; i += FETCH_LIMIT)
_executor.submit(new Go(i));
}
public synchronized void cancel() {
_canceled = true;
_executor.shutdownNow();
}
public UserPropertiesMigrationStatus getStatus() {
UserPropertiesMigrationStatus status = new UserPropertiesMigrationStatus();
status.setCanceled(isCanceled());
status.setComplete(isComplete());
status.setNumberOfUsers(getNumberOfUsers());
status.setNumberOfUsersConverted(getNumberOfUsersConverted());
status.setNumberOfUsersProcessed(getNumberOfUsersProcessed());
return status;
}
public int getNumberOfUsers() {
return _numberOfUsers;
}
public int getNumberOfUsersConverted() {
return _numberOfUsersConverted.get();
}
public int getNumberOfUsersProcessed() {
return _numberOfUsersProcessed.get();
}
public synchronized boolean isCanceled() {
return _canceled;
}
public boolean isComplete() {
return getNumberOfUsers() <= getNumberOfUsersProcessed();
}
/****
* Private Methods
****/
private void updateStatistics(int usersProcessed, int usersConverted) {
_numberOfUsersProcessed.addAndGet(usersProcessed);
_numberOfUsersConverted.addAndGet(usersConverted);
}
private class Go implements Runnable {
private int _offset;
public Go(int offset) {
_offset = offset;
}
@Override
public void run() {
_log.info("offset=" + _offset);
int usersProcessed = 0;
int usersConverted = 0;
try {
List<Integer> userIds = _dao.getAllUserIdsInRange(_offset, FETCH_LIMIT);
for (int userId : userIds) {
User user = _dao.getUserForId(userId);
_log.info("processing user: id=" + user.getId());
if (_propertiesMigration.needsMigration(user.getProperties(), _target)) {
_log.info("migrating user: id=" + user.getId());
UserProperties properties = _propertiesMigration.migrate(
user.getProperties(), _target);
user.setProperties(properties);
_dao.saveOrUpdateUser(user);
usersConverted++;
}
usersProcessed++;
}
} catch (Throwable ex) {
_log.warn("error processing users for verion migration", ex);
usersProcessed = Math.min(FETCH_LIMIT, _numberOfUsers - _offset);
}
updateStatistics(usersProcessed, usersConverted);
}
}
}