From: Mathieu Baudier Date: Wed, 10 Aug 2016 17:42:14 +0000 (+0000) Subject: Continue framework clean up. X-Git-Tag: argeo-commons-2.1.45~30 X-Git-Url: http://git.argeo.org/?a=commitdiff_plain;h=50077824250f3a7f5c092d0a5ce28a171287ac7c;p=lgpl%2Fargeo-commons.git Continue framework clean up. git-svn-id: https://svn.argeo.org/commons/trunk@9083 4cfe0d0a-d680-48aa-b62c-e0a02a3f76cc --- diff --git a/org.argeo.cms/src/org/argeo/cms/util/useradmin/UserAdminUtils.java b/org.argeo.cms/src/org/argeo/cms/util/useradmin/UserAdminUtils.java deleted file mode 100644 index 3a67717f6..000000000 --- a/org.argeo.cms/src/org/argeo/cms/util/useradmin/UserAdminUtils.java +++ /dev/null @@ -1,243 +0,0 @@ -package org.argeo.cms.util.useradmin; - -import java.security.AccessController; -import java.util.List; -import java.util.Set; - -import javax.naming.InvalidNameException; -import javax.naming.ldap.LdapName; -import javax.naming.ldap.Rdn; -import javax.security.auth.Subject; -import javax.security.auth.x500.X500Principal; - -import org.argeo.ArgeoException; -import org.argeo.cms.CmsException; -import org.argeo.cms.CmsView; -import org.argeo.cms.auth.AuthConstants; -import org.argeo.cms.auth.CurrentUser; -import org.argeo.cms.util.CmsUtils; -import org.argeo.eclipse.ui.EclipseUiUtils; -import org.argeo.jcr.JcrUtils; -import org.argeo.osgi.useradmin.LdifName; -import org.osgi.service.useradmin.Group; -import org.osgi.service.useradmin.Role; -import org.osgi.service.useradmin.User; -import org.osgi.service.useradmin.UserAdmin; - -/** Centralise common patterns to manage roles with a user admin */ -public class UserAdminUtils { - - /** Retrieves a {@link Role} given a LDAP name */ - public final static Role getRole(UserAdmin userAdmin, LdapName dn) { - Role role = userAdmin.getRole(dn.toString()); - return role; - } - - /** Retrieves the unique local username given a {@link User}. */ - public final static String getUsername(User user) { - String username = null; - if (user instanceof Group) - username = getProperty(user, LdifName.cn.name()); - else - username = getProperty(user, LdifName.uid.name()); - return username; - } - - /** - * Easily retrieves one of the {@link Role}'s property or an empty String if - * the requested property is not defined - */ - public final static String getProperty(Role role, String key) { - Object obj = role.getProperties().get(key); - if (obj != null) - return (String) obj; - else - return ""; - } - - // CENTRALIZE SOME METHODS UNTIL API IS STABLE - /** Simply checks if current user is registered */ - public static boolean isRegistered() { - return !CurrentUser.isAnonymous(); - } - - /** Simply checks if current user as a home */ - public static boolean hasHome() { - return isRegistered(); - } - - // SELF HELPERS - /** Simply retrieves the current logged-in user display name. */ - public static User getCurrentUser(UserAdmin userAdmin) { - return (User) getRole(userAdmin, getCurrentUserLdapName()); - } - - /** Simply retrieves the current logged-in user display name. */ - public static String getCurrentUserDisplayName(UserAdmin userAdmin) { - String username = getCurrentUsername(); - return getUserDisplayName(userAdmin, username); - } - - /** Simply retrieves the current logged-in user display name. */ - public static String getCurrentUserMail(UserAdmin userAdmin) { - String username = getCurrentUsername(); - return getUserMail(userAdmin, username); - } - - /** Returns the local name of the current connected user */ - public final static String getUsername(UserAdmin userAdmin) { - LdapName dn = getCurrentUserLdapName(); - return getUsername((User) getRole(userAdmin, dn)); - } - - /** Returns true if the current user is in the specified role */ - public static boolean isUserInRole(String role) { - Set roles = CurrentUser.roles(); - return roles.contains(role); - } - - /** Simply checks if current user is the same as the passed one */ - public static boolean isCurrentUser(User user) { - String userName = getProperty(user, LdifName.dn.name()); - try { - LdapName selfUserName = getCurrentUserLdapName(); - LdapName userLdapName = new LdapName(userName); - if (userLdapName.equals(selfUserName)) - return true; - else - return false; - } catch (InvalidNameException e) { - throw new ArgeoException("User " + user + " has an unvalid dn: " - + userName, e); - } - } - - public final static LdapName getCurrentUserLdapName() { - String name = getCurrentUsername(); - return getLdapName(name); - } - - /** Simply retrieves username for current user, generally a LDAP dn */ - public static String getCurrentUsername() { - Subject subject = currentSubject(); - String name = subject.getPrincipals(X500Principal.class).iterator() - .next().toString(); - return name; - } - - /** - * Fork of the {@link CurrentUser#currentSubject} method that is private. - * TODO Enhance and factorize - */ - private static Subject currentSubject() { - CmsView cmsView = CmsUtils.getCmsView(); - if (cmsView != null) - return cmsView.getSubject(); - Subject subject = Subject.getSubject(AccessController.getContext()); - if (subject != null) - return subject; - throw new CmsException("Cannot find related subject"); - } - - // HOME MANAGEMENT - /** - * Simply retrieves the *relative* path to the current user home node from - * the base home node - */ - public static String getCurrentUserHomeRelPath() { - return getHomeRelPath(getCurrentUsername()); - } - - /** - * Simply retrieves the *relative* path to the home node of a user given its - * userName - */ - public static String getHomeRelPath(String userName) { - String id = getUserUid(userName); - String currHomePath = JcrUtils.firstCharsToPath(id, 2) + "/" + id; - return currHomePath; - } - - // HELPERS TO RETRIEVE REMARKABLE PROPERTIES - /** Simply retrieves the user uid from his dn with no useradmin */ - public static String getUserUid(String dn) { - LdapName ldapName = getLdapName(dn); - Rdn last = ldapName.getRdn(ldapName.size() - 1); - if (last.getType().toLowerCase().equals(LdifName.uid.name()) - || last.getType().toLowerCase().equals(LdifName.cn.name())) - return (String) last.getValue(); - else - throw new ArgeoException("Cannot retrieve user uid, " - + "non valid dn: " + dn); - } - - /** - * Returns the local username if no user with this dn is found or if the - * found user has no defined display name - */ - public static String getUserDisplayName(UserAdmin userAdmin, String dn) { - Role user = getRole(userAdmin, getLdapName(dn)); - if (user == null) - return getUserUid(dn); - String displayName = getProperty(user, LdifName.displayName.name()); - if (EclipseUiUtils.isEmpty(displayName)) - displayName = getProperty(user, LdifName.cn.name()); - if (EclipseUiUtils.isEmpty(displayName)) - return getUserUid(dn); - else - return displayName; - } - - /** - * Returns null if no user with this dn is found or if the found user has no - * defined mail - */ - public static String getUserMail(UserAdmin userAdmin, String dn) { - Role user = getRole(userAdmin, getLdapName(dn)); - if (user == null) - return null; - else - return getProperty(user, LdifName.mail.name()); - } - - // VARIOUS UI HELPERS - public final static String buildDefaultCn(String firstName, String lastName) { - return (firstName.trim() + " " + lastName.trim() + " ").trim(); - } - - /** Simply retrieves a display name of the relevant domain */ - public final static String getDomainName(User user) { - String dn = user.getName(); - if (dn.endsWith(AuthConstants.ROLES_BASEDN)) - return "System roles"; - try { - LdapName name = new LdapName(dn); - List rdns = name.getRdns(); - String dname = null; - int i = 0; - loop: while (i < rdns.size()) { - Rdn currrRdn = rdns.get(i); - if (!LdifName.dc.name().equals(currrRdn.getType())) - break loop; - else { - String currVal = (String) currrRdn.getValue(); - dname = dname == null ? currVal : currVal + "." + dname; - } - i++; - } - return dname; - } catch (InvalidNameException e) { - throw new ArgeoException("Unable to get domain name for " + dn, e); - } - } - - // Local Helpers - /** Simply retrieves a LDAP name from a dn with no exception */ - public static LdapName getLdapName(String dn) { - try { - return new LdapName(dn); - } catch (InvalidNameException e) { - throw new ArgeoException("Cannot parse LDAP name " + dn, e); - } - } -} \ No newline at end of file diff --git a/org.argeo.cms/src/org/argeo/cms/util/useradmin/UserAdminWrapper.java b/org.argeo.cms/src/org/argeo/cms/util/useradmin/UserAdminWrapper.java deleted file mode 100644 index 8d91c71cb..000000000 --- a/org.argeo.cms/src/org/argeo/cms/util/useradmin/UserAdminWrapper.java +++ /dev/null @@ -1,105 +0,0 @@ -package org.argeo.cms.util.useradmin; - -import java.util.ArrayList; -import java.util.Dictionary; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import javax.transaction.Status; -import javax.transaction.UserTransaction; - -import org.argeo.ArgeoException; -import org.argeo.cms.auth.AuthConstants; -import org.argeo.osgi.useradmin.UserAdminConf; -import org.osgi.framework.ServiceReference; -import org.osgi.service.useradmin.UserAdmin; -import org.osgi.service.useradmin.UserAdminEvent; -import org.osgi.service.useradmin.UserAdminListener; - -/** - * Base useradmin wrapper. Implementing application might extends to add - * business specific behaviour - */ -public class UserAdminWrapper { - // private Log log = LogFactory.getLog(UserAdminWrapper.class); - - private UserAdmin userAdmin; - private ServiceReference userAdminServiceReference; - private UserTransaction userTransaction; - - /* USER ADMIN LISTENER MANAGEMENT */ - List listeners = new ArrayList(); - - // TODO implement safer mechanism - public void addListener(UserAdminListener userAdminListener) { - if (!listeners.contains(userAdminListener)) - listeners.add(userAdminListener); - } - - /** - * Starts a transaction if none already exists and notify the userAdmin - * listeners.Must be called from the UI Thread. - */ - public UserTransaction beginTransactionIfNeeded() { - try { - if (userTransaction.getStatus() == Status.STATUS_NO_TRANSACTION) { - userTransaction.begin(); - } - return userTransaction; - } catch (Exception e) { - throw new ArgeoException("Unable to begin transaction", e); - } - } - - // Expose this? - public void removeListener(UserAdminListener userAdminListener) { - if (listeners.contains(userAdminListener)) - listeners.remove(userAdminListener); - } - - public void notifyListeners(UserAdminEvent event) { - for (UserAdminListener listener : listeners) - listener.roleChanged(event); - } - - public Map getKnownBaseDns(boolean onlyWritable) { - Map dns = new HashMap(); - for (String uri : userAdminServiceReference.getPropertyKeys()) { - if (!uri.startsWith("/")) - continue; - Dictionary props = UserAdminConf.uriAsProperties(uri); - String readOnly = UserAdminConf.readOnly.getValue(props); - String baseDn = UserAdminConf.baseDn.getValue(props); - - if (onlyWritable && "true".equals(readOnly)) - continue; - if (baseDn.equalsIgnoreCase(AuthConstants.ROLES_BASEDN)) - continue; - dns.put(baseDn, uri); - } - return dns; - } - - public UserAdmin getUserAdmin() { - return userAdmin; - } - - public UserTransaction getUserTransaction() { - return userTransaction; - } - - /* DEPENDENCY INJECTION */ - public void setUserAdmin(UserAdmin userAdmin) { - this.userAdmin = userAdmin; - } - - public void setUserTransaction(UserTransaction userTransaction) { - this.userTransaction = userTransaction; - } - - public void setUserAdminServiceReference( - ServiceReference userAdminServiceReference) { - this.userAdminServiceReference = userAdminServiceReference; - } -} \ No newline at end of file diff --git a/org.argeo.security.core/src/org/argeo/osgi/transaction/simple/SimpleTransaction.java b/org.argeo.security.core/src/org/argeo/osgi/transaction/simple/SimpleTransaction.java new file mode 100644 index 000000000..0e4a58851 --- /dev/null +++ b/org.argeo.security.core/src/org/argeo/osgi/transaction/simple/SimpleTransaction.java @@ -0,0 +1,164 @@ +package org.argeo.osgi.transaction.simple; + +import java.util.ArrayList; +import java.util.List; + +import javax.transaction.HeuristicMixedException; +import javax.transaction.HeuristicRollbackException; +import javax.transaction.RollbackException; +import javax.transaction.Status; +import javax.transaction.Synchronization; +import javax.transaction.SystemException; +import javax.transaction.Transaction; +import javax.transaction.xa.XAException; +import javax.transaction.xa.XAResource; +import javax.transaction.xa.Xid; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +class SimpleTransaction implements Transaction, Status { + private final static Log log = LogFactory.getLog(SimpleTransaction.class); + + private final Xid xid; + private int status = Status.STATUS_ACTIVE; + private final List xaResources = new ArrayList(); + + private final SimpleTransactionManager transactionManager; + + public SimpleTransaction(SimpleTransactionManager transactionManager) { + this.xid = new UuidXid(); + this.transactionManager = transactionManager; + } + + @Override + public synchronized void commit() throws RollbackException, + HeuristicMixedException, HeuristicRollbackException, + SecurityException, IllegalStateException, SystemException { + status = STATUS_PREPARING; + for (XAResource xaRes : xaResources) { + if (status == STATUS_MARKED_ROLLBACK) + break; + try { + xaRes.prepare(xid); + } catch (XAException e) { + status = STATUS_MARKED_ROLLBACK; + log.error("Cannot prepare " + xaRes + " for " + xid, e); + } + } + if (status == STATUS_MARKED_ROLLBACK) { + rollback(); + throw new RollbackException(); + } + status = STATUS_PREPARED; + + status = STATUS_COMMITTING; + for (XAResource xaRes : xaResources) { + if (status == STATUS_MARKED_ROLLBACK) + break; + try { + xaRes.commit(xid, false); + } catch (XAException e) { + status = STATUS_MARKED_ROLLBACK; + log.error("Cannot prepare " + xaRes + " for " + xid, e); + } + } + if (status == STATUS_MARKED_ROLLBACK) { + rollback(); + throw new RollbackException(); + } + + // complete + status = STATUS_COMMITTED; + if (log.isDebugEnabled()) + log.debug("COMMITTED " + xid); + clearResources(XAResource.TMSUCCESS); + transactionManager.unregister(xid); + } + + @Override + public synchronized void rollback() throws IllegalStateException, + SystemException { + status = STATUS_ROLLING_BACK; + for (XAResource xaRes : xaResources) { + try { + xaRes.rollback(xid); + } catch (XAException e) { + log.error("Cannot rollback " + xaRes + " for " + xid, e); + } + } + + // complete + status = STATUS_ROLLEDBACK; + if (log.isDebugEnabled()) + log.debug("ROLLEDBACK " + xid); + clearResources(XAResource.TMFAIL); + transactionManager.unregister(xid); + } + + @Override + public synchronized boolean enlistResource(XAResource xaRes) + throws RollbackException, IllegalStateException, SystemException { + if (xaResources.add(xaRes)) { + try { + xaRes.start(getXid(), XAResource.TMNOFLAGS); + return true; + } catch (XAException e) { + log.error("Cannot enlist " + xaRes, e); + return false; + } + } else + return false; + } + + @Override + public synchronized boolean delistResource(XAResource xaRes, int flag) + throws IllegalStateException, SystemException { + if (xaResources.remove(xaRes)) { + try { + xaRes.end(getXid(), flag); + } catch (XAException e) { + log.error("Cannot delist " + xaRes, e); + return false; + } + return true; + } else + return false; + } + + protected void clearResources(int flag) { + for (XAResource xaRes : xaResources) + try { + xaRes.end(getXid(), flag); + } catch (XAException e) { + log.error("Cannot end " + xaRes, e); + } + xaResources.clear(); + } + + @Override + public synchronized int getStatus() throws SystemException { + return status; + } + + @Override + public void registerSynchronization(Synchronization sync) + throws RollbackException, IllegalStateException, SystemException { + throw new UnsupportedOperationException(); + } + + @Override + public void setRollbackOnly() throws IllegalStateException, SystemException { + status = STATUS_MARKED_ROLLBACK; + } + + @Override + public int hashCode() { + return xid.hashCode(); + } + + Xid getXid() { + return xid; + } + +} diff --git a/org.argeo.security.core/src/org/argeo/osgi/transaction/simple/SimpleTransactionManager.java b/org.argeo.security.core/src/org/argeo/osgi/transaction/simple/SimpleTransactionManager.java new file mode 100644 index 000000000..696f0c099 --- /dev/null +++ b/org.argeo.security.core/src/org/argeo/osgi/transaction/simple/SimpleTransactionManager.java @@ -0,0 +1,173 @@ +package org.argeo.osgi.transaction.simple; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +import javax.transaction.HeuristicMixedException; +import javax.transaction.HeuristicRollbackException; +import javax.transaction.InvalidTransactionException; +import javax.transaction.NotSupportedException; +import javax.transaction.RollbackException; +import javax.transaction.Status; +import javax.transaction.Synchronization; +import javax.transaction.SystemException; +import javax.transaction.Transaction; +import javax.transaction.TransactionManager; +import javax.transaction.TransactionSynchronizationRegistry; +import javax.transaction.UserTransaction; +import javax.transaction.xa.Xid; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +public class SimpleTransactionManager implements TransactionManager, UserTransaction { + private final static Log log = LogFactory.getLog(SimpleTransactionManager.class); + + private ThreadLocal current = new ThreadLocal(); + + private Map knownTransactions = Collections + .synchronizedMap(new HashMap()); + private SyncRegistry syncRegistry = new SyncRegistry(); + + @Override + public void begin() throws NotSupportedException, SystemException { + if (getCurrent() != null) + throw new NotSupportedException("Nested transactions are not supported"); + SimpleTransaction transaction = new SimpleTransaction(this); + knownTransactions.put(transaction.getXid(), transaction); + current.set(transaction); + if (log.isDebugEnabled()) + log.debug("STARTED " + transaction.getXid()); + } + + @Override + public void commit() throws RollbackException, HeuristicMixedException, HeuristicRollbackException, + SecurityException, IllegalStateException, SystemException { + if (getCurrent() == null) + throw new IllegalStateException("No transaction registered with the current thread."); + getCurrent().commit(); + } + + @Override + public int getStatus() throws SystemException { + if (getCurrent() == null) + return Status.STATUS_NO_TRANSACTION; + return getTransaction().getStatus(); + } + + @Override + public Transaction getTransaction() throws SystemException { + return getCurrent(); + } + + protected SimpleTransaction getCurrent() throws SystemException { + SimpleTransaction transaction = current.get(); + if (transaction == null) + return null; + int status = transaction.getStatus(); + if (Status.STATUS_COMMITTED == status || Status.STATUS_ROLLEDBACK == status) { + current.remove(); + return null; + } + return transaction; + } + + void unregister(Xid xid) { + knownTransactions.remove(xid); + } + + @Override + public void resume(Transaction tobj) throws InvalidTransactionException, IllegalStateException, SystemException { + if (getCurrent() != null) + throw new IllegalStateException("Transaction " + current.get() + " already registered"); + current.set((SimpleTransaction) tobj); + } + + @Override + public void rollback() throws IllegalStateException, SecurityException, SystemException { + if (getCurrent() == null) + throw new IllegalStateException("No transaction registered with the current thread."); + getCurrent().rollback(); + } + + @Override + public void setRollbackOnly() throws IllegalStateException, SystemException { + if (getCurrent() == null) + throw new IllegalStateException("No transaction registered with the current thread."); + getCurrent().setRollbackOnly(); + } + + @Override + public void setTransactionTimeout(int seconds) throws SystemException { + throw new UnsupportedOperationException(); + } + + @Override + public Transaction suspend() throws SystemException { + Transaction transaction = getCurrent(); + current.remove(); + return transaction; + } + + public TransactionSynchronizationRegistry getTsr() { + return syncRegistry; + } + + private class SyncRegistry implements TransactionSynchronizationRegistry { + @Override + public Object getTransactionKey() { + try { + SimpleTransaction transaction = getCurrent(); + if (transaction == null) + return null; + return getCurrent().getXid(); + } catch (SystemException e) { + throw new RuntimeException("Cannot get transaction key", e); + } + } + + @Override + public void putResource(Object key, Object value) { + throw new UnsupportedOperationException(); + } + + @Override + public Object getResource(Object key) { + throw new UnsupportedOperationException(); + } + + @Override + public void registerInterposedSynchronization(Synchronization sync) { + throw new UnsupportedOperationException(); + } + + @Override + public int getTransactionStatus() { + try { + return getStatus(); + } catch (SystemException e) { + throw new RuntimeException("Cannot get status", e); + } + } + + @Override + public boolean getRollbackOnly() { + try { + return getStatus() == Status.STATUS_MARKED_ROLLBACK; + } catch (SystemException e) { + throw new RuntimeException("Cannot get status", e); + } + } + + @Override + public void setRollbackOnly() { + try { + getCurrent().setRollbackOnly(); + } catch (Exception e) { + throw new RuntimeException("Cannot set rollback only", e); + } + } + + } +} diff --git a/org.argeo.security.core/src/org/argeo/osgi/transaction/simple/UuidXid.java b/org.argeo.security.core/src/org/argeo/osgi/transaction/simple/UuidXid.java new file mode 100644 index 000000000..cf355551f --- /dev/null +++ b/org.argeo.security.core/src/org/argeo/osgi/transaction/simple/UuidXid.java @@ -0,0 +1,132 @@ +package org.argeo.osgi.transaction.simple; + +import java.io.Serializable; +import java.nio.ByteBuffer; +import java.util.Arrays; +import java.util.UUID; + +import javax.transaction.xa.Xid; + +/** + * Implementation of {@link Xid} based on {@link UUID}, using max significant + * bits as global transaction id, and least significant bits as branch + * qualifier. + */ +public class UuidXid implements Xid, Serializable { + private static final long serialVersionUID = -5380531989917886819L; + public final static int FORMAT = (int) serialVersionUID; + + private final static int BYTES_PER_LONG = Long.SIZE / Byte.SIZE; + + private final int format; + private final byte[] globalTransactionId; + private final byte[] branchQualifier; + private final String uuid; + private final int hashCode; + + public UuidXid() { + this(UUID.randomUUID()); + } + + public UuidXid(UUID uuid) { + this.format = FORMAT; + this.globalTransactionId = uuidToBytes(uuid.getMostSignificantBits()); + this.branchQualifier = uuidToBytes(uuid.getLeastSignificantBits()); + this.uuid = uuid.toString(); + this.hashCode = uuid.hashCode(); + } + + public UuidXid(Xid xid) { + this(xid.getFormatId(), xid.getGlobalTransactionId(), xid + .getBranchQualifier()); + } + + private UuidXid(int format, byte[] globalTransactionId, + byte[] branchQualifier) { + this.format = format; + this.globalTransactionId = globalTransactionId; + this.branchQualifier = branchQualifier; + this.uuid = bytesToUUID(globalTransactionId, branchQualifier) + .toString(); + this.hashCode = uuid.hashCode(); + } + + @Override + public int getFormatId() { + return format; + } + + @Override + public byte[] getGlobalTransactionId() { + return Arrays.copyOf(globalTransactionId, globalTransactionId.length); + } + + @Override + public byte[] getBranchQualifier() { + return Arrays.copyOf(branchQualifier, branchQualifier.length); + } + + @Override + public int hashCode() { + return hashCode; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj instanceof UuidXid) { + UuidXid that = (UuidXid) obj; + return Arrays.equals(globalTransactionId, that.globalTransactionId) + && Arrays.equals(branchQualifier, that.branchQualifier); + } + if (obj instanceof Xid) { + Xid that = (Xid) obj; + return Arrays.equals(globalTransactionId, + that.getGlobalTransactionId()) + && Arrays + .equals(branchQualifier, that.getBranchQualifier()); + } + return uuid.equals(obj.toString()); + } + + @Override + protected Object clone() throws CloneNotSupportedException { + return new UuidXid(format, globalTransactionId, branchQualifier); + } + + @Override + public String toString() { + return uuid; + } + + public UUID asUuid() { + return bytesToUUID(globalTransactionId, branchQualifier); + } + + public static byte[] uuidToBytes(long bits) { + ByteBuffer buffer = ByteBuffer.allocate(BYTES_PER_LONG); + buffer.putLong(0, bits); + return buffer.array(); + } + + public static UUID bytesToUUID(byte[] most, byte[] least) { + if (most.length < BYTES_PER_LONG) + most = Arrays.copyOf(most, BYTES_PER_LONG); + if (least.length < BYTES_PER_LONG) + least = Arrays.copyOf(least, BYTES_PER_LONG); + ByteBuffer buffer = ByteBuffer.allocate(2 * BYTES_PER_LONG); + buffer.put(most, 0, BYTES_PER_LONG); + buffer.put(least, 0, BYTES_PER_LONG); + buffer.flip(); + return new UUID(buffer.getLong(), buffer.getLong()); + } + + // public static void main(String[] args) { + // UUID uuid = UUID.randomUUID(); + // System.out.println(uuid); + // uuid = bytesToUUID(uuidToBytes(uuid.getMostSignificantBits()), + // uuidToBytes(uuid.getLeastSignificantBits())); + // System.out.println(uuid); + // } +}