Author: angela
Date: Tue Oct 23 16:11:33 2012
New Revision: 1401335
URL: http://svn.apache.org/viewvc?rev=1401335&view=rev
Log:
OAK-91 - Implement Authentication Support (WIP)
Added:
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/DefaultSyncHandler.java
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/ExternalGroup.java
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/ExternalLoginModule.java
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/ExternalUser.java
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/SyncException.java
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/SyncHandler.java
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/SyncMode.java
Added:
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/DefaultSyncHandler.java
URL:
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/DefaultSyncHandler.java?rev=1401335&view=auto
==============================================================================
---
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/DefaultSyncHandler.java
(added)
+++
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/DefaultSyncHandler.java
Tue Oct 23 16:11:33 2012
@@ -0,0 +1,180 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.apache.jackrabbit.oak.spi.security.authentication.external;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+import javax.annotation.CheckForNull;
+import javax.jcr.RepositoryException;
+import javax.jcr.Value;
+import javax.jcr.ValueFactory;
+
+import org.apache.jackrabbit.api.security.user.Authorizable;
+import org.apache.jackrabbit.api.security.user.Group;
+import org.apache.jackrabbit.api.security.user.User;
+import org.apache.jackrabbit.api.security.user.UserManager;
+import org.apache.jackrabbit.oak.api.Root;
+import org.apache.jackrabbit.oak.namepath.NamePathMapper;
+import org.apache.jackrabbit.oak.plugins.value.ValueFactoryImpl;
+import org.apache.jackrabbit.oak.spi.security.ConfigurationParameters;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * DefaultSyncHandler... TODO
+ */
+public class DefaultSyncHandler implements SyncHandler {
+
+ /**
+ * logger instance
+ */
+ private static final Logger log =
LoggerFactory.getLogger(DefaultSyncHandler.class);
+
+ private UserManager userManager;
+ private Root root;
+ private SyncMode mode;
+ private ConfigurationParameters options;
+
+ private ValueFactory valueFactory;
+
+ private boolean initialized;
+
+ @Override
+ public boolean initialize(UserManager userManager, Root root, SyncMode
mode,
+ ConfigurationParameters options) throws
SyncException {
+ if (userManager == null || root == null) {
+ throw new SyncException("Error while initializing sync handler.");
+ }
+ this.userManager = userManager;
+ this.root = root;
+ this.mode = mode;
+ this.options = (options == null) ? ConfigurationParameters.EMPTY :
options;
+
+ valueFactory = new ValueFactoryImpl(root.getBlobFactory(),
NamePathMapper.DEFAULT);
+ initialized = true;
+ return true;
+ }
+
+ @Override
+ public boolean sync(ExternalUser externalUser) throws SyncException {
+ checkInitialized();
+ try {
+ User user = getUser(externalUser);
+ if (user == null) {
+ createUser(externalUser);
+ } else {
+ updateUser(externalUser, user);
+ }
+ return true;
+ } catch (RepositoryException e) {
+ throw new SyncException(e);
+ }
+ }
+
+
//--------------------------------------------------------------------------
+ private void checkInitialized() {
+ if (!initialized) {
+ throw new IllegalStateException("not initialized");
+ }
+ }
+
+ @CheckForNull
+ private User getUser(ExternalUser externalUser) throws RepositoryException
{
+ // TODO: deal with colliding authorizable that is group.
+
+ Authorizable authorizable =
userManager.getAuthorizable(externalUser.getId());
+ if (authorizable == null) {
+ authorizable =
userManager.getAuthorizable(externalUser.getPrincipal());
+ }
+
+ return (authorizable == null) ? null : (User) authorizable;
+ }
+
+ @CheckForNull
+ private User createUser(ExternalUser externalUser) throws
RepositoryException, SyncException {
+ if (mode.contains(SyncMode.MODE_CREATE_USER)) {
+ User user = userManager.createUser(externalUser.getId(), null,
externalUser.getPrincipal(), externalUser.getPath());
+ syncAuthorizable(externalUser, user);
+ return user;
+ } else {
+ return null;
+ }
+ }
+
+ @CheckForNull
+ private Group createGroup(ExternalGroup externalGroup) throws
RepositoryException, SyncException {
+ if (mode.contains(SyncMode.MODE_CREATE_GROUPS)) {
+ Group group = userManager.createGroup(externalGroup.getId(),
externalGroup.getPrincipal(), externalGroup.getPath());
+ syncAuthorizable(externalGroup, group);
+ return group;
+ } else {
+ return null;
+ }
+ }
+
+ private void updateUser(ExternalUser externalUser, User user) throws
RepositoryException, SyncException {
+ if (mode.contains(SyncMode.MODE_UPDATE)) {
+ syncAuthorizable(externalUser, user);
+ }
+ }
+
+ private void syncAuthorizable(ExternalUser externalUser, Authorizable
authorizable) throws RepositoryException, SyncException {
+ for (ExternalGroup externalGroup : externalUser.getGroups()) {
+ String groupId = externalGroup.getId();
+ Group group;
+ Authorizable a = userManager.getAuthorizable(groupId);
+ if (a == null) {
+ group = createGroup(externalGroup);
+ } else {
+ group = (a.isGroup()) ? (Group) a : null;
+ }
+
+ if (group != null) {
+ group.addMember(authorizable);
+ } else {
+ log.debug("No such group " + groupId + "; Ignoring group
membership.");
+ }
+ }
+
+ Map<String, ?> properties = externalUser.getProperties();
+ for (String key : properties.keySet()) {
+ Object prop = properties.get(key);
+ if (prop instanceof Collection) {
+ Value[] values = createValues((Collection) prop);
+ authorizable.setProperty(key, values);
+ } else {
+ Value value = createValue(prop);
+ authorizable.setProperty(key, value);
+ }
+ }
+ }
+
+ private Value createValue(Object propValue) {
+ // TODO
+ return null;
+ }
+
+ private Value[] createValues(Collection<?> propValues) {
+ List<Value> values = new ArrayList<Value>();
+ for (Object obj : propValues) {
+ values.add(createValue(obj));
+ }
+ return values.toArray(new Value[values.size()]);
+ }
+}
\ No newline at end of file
Added:
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/ExternalGroup.java
URL:
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/ExternalGroup.java?rev=1401335&view=auto
==============================================================================
---
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/ExternalGroup.java
(added)
+++
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/ExternalGroup.java
Tue Oct 23 16:11:33 2012
@@ -0,0 +1,23 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.apache.jackrabbit.oak.spi.security.authentication.external;
+
+/**
+ * ExternalGroup... TODO
+ */
+public interface ExternalGroup extends ExternalUser {
+}
\ No newline at end of file
Added:
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/ExternalLoginModule.java
URL:
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/ExternalLoginModule.java?rev=1401335&view=auto
==============================================================================
---
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/ExternalLoginModule.java
(added)
+++
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/ExternalLoginModule.java
Tue Oct 23 16:11:33 2012
@@ -0,0 +1,138 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.apache.jackrabbit.oak.spi.security.authentication.external;
+
+import java.util.Collections;
+import java.util.Set;
+import javax.jcr.SimpleCredentials;
+import javax.security.auth.login.LoginException;
+
+import org.apache.jackrabbit.oak.api.CommitFailedException;
+import org.apache.jackrabbit.oak.api.Root;
+import
org.apache.jackrabbit.oak.spi.security.authentication.AbstractLoginModule;
+import org.apache.jackrabbit.util.Text;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * ExternalLoginModule... TODO
+ */
+public abstract class ExternalLoginModule extends AbstractLoginModule {
+
+ /**
+ * logger instance
+ */
+ private static final Logger log =
LoggerFactory.getLogger(ExternalLoginModule.class);
+
+ public static final String PARAM_SYNC_MODE = "syncMode";
+ public static final SyncMode DEFAULT_SYNC_MODE = SyncMode.DEFAULT_SYNC;
+
+ private static final String PARAM_SYNC_HANDLER = "syncHandler";
+ private static final String DEFAULT_SYNC_HANDLER =
DefaultSyncHandler.class.getName();
+
+ //------------------------------------------------< ExternalLoginModule
>---
+ /**
+ * TODO
+ *
+ * @return
+ */
+ protected abstract boolean loginSucceeded();
+
+ /**
+ * TODO
+ *
+ * @return
+ */
+ protected abstract ExternalUser getExternalUser();
+
+ /**
+ * TODO
+ *
+ * @return
+ * @throws SyncException
+ */
+ protected SyncHandler getSyncHandler() throws SyncException {
+ String shClass = options.getConfigValue(PARAM_SYNC_HANDLER,
DEFAULT_SYNC_HANDLER);
+ Object syncHandler;
+ try {
+ syncHandler = Class.forName(shClass).newInstance();
+ } catch (Exception e) {
+ throw new SyncException("Error while getting SyncHandler:", e);
+ }
+
+ if (syncHandler.getClass().isAssignableFrom(SyncHandler.class)) {
+ return (SyncHandler) syncHandler;
+ } else {
+ throw new SyncException("Invalid SyncHandler class configured: " +
syncHandler.getClass().getName());
+ }
+ }
+
+ //------------------------------------------------< AbstractLoginModule
>---
+
+ /**
+ * Default implementation of the {@link #getSupportedCredentials()} method
+ * that only lists {@link SimpleCredentials} as supported. Subclasses that
+ * wish to support other or additional credential implementations should
+ * override this method.
+ *
+ * @return An immutable set containing only the {@link SimpleCredentials}
class.
+ */
+ @Override
+ protected Set<Class> getSupportedCredentials() {
+ Class scClass = SimpleCredentials.class;
+ return Collections.singleton(scClass);
+ }
+
+ //--------------------------------------------------------< LoginModule
>---
+
+ /**
+ * TODO
+ *
+ * @return
+ * @throws LoginException
+ */
+ @Override
+ public boolean commit() throws LoginException {
+ if (!loginSucceeded()) {
+ return false;
+ }
+
+ try {
+ SyncHandler handler = getSyncHandler();
+ Root root = getRoot();
+ String smValue = options.getConfigValue(PARAM_SYNC_MODE, null);
+ SyncMode syncMode;
+ if (smValue == null) {
+ syncMode = DEFAULT_SYNC_MODE;
+ } else {
+ syncMode = SyncMode.fromStrings(Text.explode(smValue, ',',
false));
+ }
+ if (handler.initialize(getUserManager(), root, syncMode, options))
{
+ handler.sync(getExternalUser());
+ root.commit();
+ return true;
+ } else {
+ log.warn("Failed to initialize sync handler.");
+ return false;
+ }
+ } catch (SyncException e) {
+ throw new LoginException("User synchronization failed: " + e);
+ } catch (CommitFailedException e) {
+ throw new LoginException("User synchronization failed: " + e);
+ }
+ }
+}
\ No newline at end of file
Added:
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/ExternalUser.java
URL:
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/ExternalUser.java?rev=1401335&view=auto
==============================================================================
---
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/ExternalUser.java
(added)
+++
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/ExternalUser.java
Tue Oct 23 16:11:33 2012
@@ -0,0 +1,37 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.apache.jackrabbit.oak.spi.security.authentication.external;
+
+import java.security.Principal;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * ExternalUser... TODO
+ */
+public interface ExternalUser {
+
+ String getId();
+
+ Principal getPrincipal();
+
+ String getPath();
+
+ Set<ExternalGroup> getGroups();
+
+ Map<String, ?> getProperties();
+}
\ No newline at end of file
Added:
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/SyncException.java
URL:
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/SyncException.java?rev=1401335&view=auto
==============================================================================
---
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/SyncException.java
(added)
+++
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/SyncException.java
Tue Oct 23 16:11:33 2012
@@ -0,0 +1,35 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.apache.jackrabbit.oak.spi.security.authentication.external;
+
+/**
+ * SyncException... TODO
+ */
+public class SyncException extends Exception {
+
+ public SyncException(String s) {
+ super(s);
+ }
+
+ public SyncException(Throwable throwable) {
+ super(throwable);
+ }
+
+ public SyncException(String s, Throwable throwable) {
+ super(s, throwable);
+ }
+}
\ No newline at end of file
Added:
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/SyncHandler.java
URL:
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/SyncHandler.java?rev=1401335&view=auto
==============================================================================
---
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/SyncHandler.java
(added)
+++
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/SyncHandler.java
Tue Oct 23 16:11:33 2012
@@ -0,0 +1,32 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.apache.jackrabbit.oak.spi.security.authentication.external;
+
+import org.apache.jackrabbit.api.security.user.UserManager;
+import org.apache.jackrabbit.oak.api.Root;
+import org.apache.jackrabbit.oak.spi.security.ConfigurationParameters;
+
+/**
+ * SyncHandler... TODO
+ */
+public interface SyncHandler {
+
+ boolean initialize(UserManager userManager, Root root, SyncMode mode,
+ ConfigurationParameters options) throws SyncException;
+
+ boolean sync(ExternalUser externalUser) throws SyncException;
+}
\ No newline at end of file
Added:
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/SyncMode.java
URL:
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/SyncMode.java?rev=1401335&view=auto
==============================================================================
---
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/SyncMode.java
(added)
+++
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/SyncMode.java
Tue Oct 23 16:11:33 2012
@@ -0,0 +1,78 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.apache.jackrabbit.oak.spi.security.authentication.external;
+
+/**
+ * SyncMode... TODO: define sync-modes
+ */
+public class SyncMode {
+
+ public static final int MODE_NO_SYNC = 0;
+ public static final int MODE_CREATE_USER = 1;
+ public static final int MODE_CREATE_GROUPS = 2;
+ public static final int MODE_UPDATE = 4;
+
+ public static final String CREATE_USER_NAME = "createUser";
+ public static final String CREATE_GROUP_NAME = "createGroup";
+ public static final String UPDATE = "update";
+
+ public static final SyncMode DEFAULT_SYNC = new
SyncMode(MODE_CREATE_USER|MODE_CREATE_GROUPS|MODE_UPDATE);
+
+ private final int mode;
+
+ private SyncMode(int mode) {
+ this.mode = mode;
+ }
+
+ public boolean contains(int mode) {
+ return (this.mode & mode) == mode;
+ }
+
+ public static SyncMode fromString(String name) {
+ int mode;
+ if (CREATE_USER_NAME.equals(name)) {
+ mode = MODE_CREATE_USER;
+ } else if (CREATE_GROUP_NAME.equals(name)) {
+ mode = MODE_CREATE_GROUPS;
+ } else if (UPDATE.equals(name)) {
+ mode = MODE_UPDATE;
+ } else {
+ throw new IllegalArgumentException("invalid sync mode name " +
name);
+ }
+ return fromInt(mode);
+ }
+
+ public static SyncMode fromStrings(String[] names) {
+ int mode = MODE_NO_SYNC;
+ for (String name : names) {
+ mode |= fromString(name.trim()).mode;
+ }
+ return new SyncMode(mode);
+ }
+
+ private static SyncMode fromInt(int mode) {
+ if (mode == DEFAULT_SYNC.mode) {
+ return DEFAULT_SYNC;
+ }
+
+ if (mode < 0 || mode > DEFAULT_SYNC.mode) {
+ throw new IllegalArgumentException("invalid sync mode: " + mode);
+ } else {
+ return new SyncMode(mode);
+ }
+ }
+}
\ No newline at end of file