diff options
author | Simon Brandhof <simon.brandhof@sonarsource.com> | 2016-06-03 09:03:42 +0200 |
---|---|---|
committer | Simon Brandhof <simon.brandhof@sonarsource.com> | 2016-06-06 09:41:01 +0200 |
commit | 056b5b562e0c97e5e8c8fe598883c94b07a2bb27 (patch) | |
tree | 1eca1ad8256c664e222fa4945ce5ab6cede04c1e /server | |
parent | 50c0a193b504d2eef6c57cabe4747ba11ded8657 (diff) | |
download | sonarqube-056b5b562e0c97e5e8c8fe598883c94b07a2bb27.tar.gz sonarqube-056b5b562e0c97e5e8c8fe598883c94b07a2bb27.zip |
Move org.sonar.core.user classes outside sonar-db
Diffstat (limited to 'server')
13 files changed, 491 insertions, 9 deletions
diff --git a/server/sonar-ce/src/main/java/org/sonar/ce/container/ComputeEngineContainerImpl.java b/server/sonar-ce/src/main/java/org/sonar/ce/container/ComputeEngineContainerImpl.java index 4fe2f430a13..689e56ee4d7 100644 --- a/server/sonar-ce/src/main/java/org/sonar/ce/container/ComputeEngineContainerImpl.java +++ b/server/sonar-ce/src/main/java/org/sonar/ce/container/ComputeEngineContainerImpl.java @@ -50,8 +50,6 @@ import org.sonar.core.platform.Module; import org.sonar.core.platform.PluginClassloaderFactory; import org.sonar.core.platform.PluginLoader; import org.sonar.core.timemachine.Periods; -import org.sonar.core.user.DefaultUserFinder; -import org.sonar.core.user.DeprecatedUserFinder; import org.sonar.core.util.UuidFactoryImpl; import org.sonar.db.DaoModule; import org.sonar.db.DatabaseChecker; @@ -126,6 +124,8 @@ import org.sonar.server.rule.index.RuleIndexer; import org.sonar.server.search.EsSearchModule; import org.sonar.server.startup.LogServerId; import org.sonar.server.test.index.TestIndexer; +import org.sonar.server.user.DefaultUserFinder; +import org.sonar.server.user.DeprecatedUserFinder; import org.sonar.server.user.index.UserIndex; import org.sonar.server.user.index.UserIndexer; import org.sonar.server.view.index.ViewIndex; diff --git a/server/sonar-server/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevel4.java b/server/sonar-server/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevel4.java index 9e70631f342..ef7a11a4c0c 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevel4.java +++ b/server/sonar-server/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevel4.java @@ -32,8 +32,6 @@ import org.sonar.api.server.rule.RulesDefinitionXmlLoader; import org.sonar.ce.CeModule; import org.sonar.core.component.DefaultResourceTypes; import org.sonar.core.timemachine.Periods; -import org.sonar.core.user.DefaultUserFinder; -import org.sonar.core.user.DeprecatedUserFinder; import org.sonar.db.permission.PermissionRepository; import org.sonar.server.activity.ActivityService; import org.sonar.server.activity.RubyQProfileActivityService; @@ -278,7 +276,9 @@ import org.sonar.server.ui.ws.GlobalNavigationAction; import org.sonar.server.ui.ws.NavigationWs; import org.sonar.server.ui.ws.SettingsNavigationAction; import org.sonar.server.updatecenter.ws.UpdateCenterWs; +import org.sonar.server.user.DefaultUserFinder; import org.sonar.server.user.DefaultUserService; +import org.sonar.server.user.DeprecatedUserFinder; import org.sonar.server.user.GroupMembershipFinder; import org.sonar.server.user.GroupMembershipService; import org.sonar.server.user.NewUserNotifier; diff --git a/server/sonar-server/src/main/java/org/sonar/server/user/DefaultUserFinder.java b/server/sonar-server/src/main/java/org/sonar/server/user/DefaultUserFinder.java new file mode 100644 index 00000000000..83e93033573 --- /dev/null +++ b/server/sonar-server/src/main/java/org/sonar/server/user/DefaultUserFinder.java @@ -0,0 +1,69 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.server.user; + +import com.google.common.collect.Lists; +import java.util.Collection; +import java.util.List; +import javax.annotation.CheckForNull; +import org.sonar.api.user.User; +import org.sonar.api.user.UserFinder; +import org.sonar.api.user.UserQuery; +import org.sonar.db.user.UserDao; +import org.sonar.db.user.UserDto; + +/** + * @since 3.6 + */ +public class DefaultUserFinder implements UserFinder { + + private final UserDao userDao; + + public DefaultUserFinder(UserDao userDao) { + this.userDao = userDao; + } + + @Override + @CheckForNull + public User findByLogin(String login) { + UserDto dto = userDao.selectActiveUserByLogin(login); + return dto != null ? dto.toUser() : null; + } + + @Override + public List<User> findByLogins(List<String> logins) { + List<UserDto> dtos = userDao.selectByLogins(logins); + return toUsers(dtos); + } + + @Override + public List<User> find(UserQuery query) { + List<UserDto> dtos = userDao.selectUsers(query); + return toUsers(dtos); + } + + private static List<User> toUsers(Collection<UserDto> dtos) { + List<User> users = Lists.newArrayList(); + for (UserDto dto : dtos) { + users.add(dto.toUser()); + } + return users; + } +} diff --git a/server/sonar-server/src/main/java/org/sonar/server/user/DeprecatedUserFinder.java b/server/sonar-server/src/main/java/org/sonar/server/user/DeprecatedUserFinder.java new file mode 100644 index 00000000000..31a0174d198 --- /dev/null +++ b/server/sonar-server/src/main/java/org/sonar/server/user/DeprecatedUserFinder.java @@ -0,0 +1,58 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.server.user; + +import javax.annotation.Nullable; +import org.sonar.api.database.model.User; +import org.sonar.api.security.UserFinder; +import org.sonar.db.user.UserDao; +import org.sonar.db.user.UserDto; + +/** + * @since 2.10 + */ +public class DeprecatedUserFinder implements UserFinder { + + private final UserDao userDao; + + public DeprecatedUserFinder(UserDao userDao) { + this.userDao = userDao; + } + + @Override + public User findById(int id) { + return copy(userDao.selectUserById(id)); + } + + @Override + public User findByLogin(String login) { + return copy(userDao.selectActiveUserByLogin(login)); + } + + private static User copy(@Nullable UserDto dto) { + if (dto != null) { + User user = new User().setEmail(dto.getEmail()).setLogin(dto.getLogin()).setName(dto.getName()); + user.setId(dto.getId().intValue()); + return user; + } + return null; + } + +} diff --git a/server/sonar-server/src/main/java/org/sonar/server/user/GroupMembership.java b/server/sonar-server/src/main/java/org/sonar/server/user/GroupMembership.java new file mode 100644 index 00000000000..9c384d1f7af --- /dev/null +++ b/server/sonar-server/src/main/java/org/sonar/server/user/GroupMembership.java @@ -0,0 +1,93 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.server.user; + +import javax.annotation.CheckForNull; +import javax.annotation.Nullable; +import org.apache.commons.lang.builder.ToStringBuilder; +import org.apache.commons.lang.builder.ToStringStyle; + +public class GroupMembership { + + private Long id; + private String name; + private String description; + private boolean isMember; + + public Long id() { + return id; + } + + public GroupMembership setId(Long id) { + this.id = id; + return this; + } + + public String name() { + return name; + } + + public GroupMembership setName(String name) { + this.name = name; + return this; + } + + @CheckForNull + public String description() { + return description; + } + + public GroupMembership setDescription(@Nullable String description) { + this.description = description; + return this; + } + + public boolean isMember() { + return isMember; + } + + public GroupMembership setMember(boolean isMember) { + this.isMember = isMember; + return this; + } + + @Override + public String toString() { + return ToStringBuilder.reflectionToString(this, ToStringStyle.SHORT_PREFIX_STYLE); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + GroupMembership that = (GroupMembership) o; + return name.equals(that.name); + } + + @Override + public int hashCode() { + return name.hashCode(); + } + +} diff --git a/server/sonar-server/src/main/java/org/sonar/server/user/GroupMembershipFinder.java b/server/sonar-server/src/main/java/org/sonar/server/user/GroupMembershipFinder.java index f6dfe9e4cc8..6dccec36024 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/user/GroupMembershipFinder.java +++ b/server/sonar-server/src/main/java/org/sonar/server/user/GroupMembershipFinder.java @@ -21,7 +21,6 @@ package org.sonar.server.user; import java.util.List; import org.sonar.api.server.ServerSide; -import org.sonar.core.user.GroupMembership; import org.sonar.db.user.GroupMembershipDao; import org.sonar.db.user.GroupMembershipDto; import org.sonar.db.user.GroupMembershipQuery; @@ -88,8 +87,12 @@ public class GroupMembershipFinder { private static List<GroupMembership> toGroupMembership(List<GroupMembershipDto> dtos) { List<GroupMembership> groups = newArrayList(); - for (GroupMembershipDto groupMembershipDto : dtos) { - groups.add(groupMembershipDto.toGroupMembership()); + for (GroupMembershipDto dto : dtos) { + groups.add(new GroupMembership() + .setId(dto.getId()) + .setName(dto.getName()) + .setDescription(dto.getDescription()) + .setMember(dto.getUserId() != null)); } return groups; } diff --git a/server/sonar-server/src/test/java/org/sonar/server/user/DefaultUserFinderTest.java b/server/sonar-server/src/test/java/org/sonar/server/user/DefaultUserFinderTest.java new file mode 100644 index 00000000000..e8bdd7d059e --- /dev/null +++ b/server/sonar-server/src/test/java/org/sonar/server/user/DefaultUserFinderTest.java @@ -0,0 +1,66 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.server.user; + +import java.util.Arrays; +import java.util.Collection; +import org.junit.Test; +import org.sonar.api.user.User; +import org.sonar.api.user.UserQuery; +import org.sonar.db.user.UserDao; +import org.sonar.db.user.UserDto; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +public class DefaultUserFinderTest { + UserDao dao = mock(UserDao.class); + DefaultUserFinder finder = new DefaultUserFinder(dao); + + @Test + public void findByLogin() { + UserDto dto = new UserDto().setLogin("david").setName("David").setEmail("dav@id.com"); + when(dao.selectActiveUserByLogin("david")).thenReturn(dto); + + assertThat(finder.findByLogin("david").name()).isEqualTo("David"); + } + + @Test + public void findByLogins() { + UserDto david = new UserDto().setLogin("david").setName("David").setEmail("dav@id.com"); + UserDto john = new UserDto().setLogin("john").setName("John").setEmail("jo@hn.com"); + when(dao.selectByLogins(Arrays.asList("david", "john"))).thenReturn(Arrays.asList(david, john)); + + Collection<User> users = finder.findByLogins(Arrays.asList("david", "john")); + assertThat(users).hasSize(2); + for (User user : users) { + assertThat(user.login()).isIn("david", "john"); + } + } + + @Test + public void findByQuery() { + UserQuery query = UserQuery.builder().logins("simon").build(); + finder.find(query); + verify(dao).selectUsers(query); + } +} diff --git a/server/sonar-server/src/test/java/org/sonar/server/user/DefaultUserTest.java b/server/sonar-server/src/test/java/org/sonar/server/user/DefaultUserTest.java new file mode 100644 index 00000000000..ed7ad739a2f --- /dev/null +++ b/server/sonar-server/src/test/java/org/sonar/server/user/DefaultUserTest.java @@ -0,0 +1,53 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.server.user; + +import org.junit.Test; +import org.sonar.core.user.DefaultUser; + +import static org.assertj.core.api.Assertions.assertThat; + +public class DefaultUserTest { + @Test + public void test_object_methods() throws Exception { + DefaultUser john = new DefaultUser().setLogin("john").setName("John"); + DefaultUser eric = new DefaultUser().setLogin("eric").setName("Eric"); + + assertThat(john).isEqualTo(john); + assertThat(john).isNotEqualTo(eric); + assertThat(john.hashCode()).isEqualTo(john.hashCode()); + assertThat(john.toString()).contains("login=john").contains("name=John"); + } + + @Test + public void test_email() { + DefaultUser user = new DefaultUser(); + assertThat(user.email()).isNull(); + + user.setEmail(""); + assertThat(user.email()).isNull(); + + user.setEmail(" "); + assertThat(user.email()).isNull(); + + user.setEmail("s@b.com"); + assertThat(user.email()).isEqualTo("s@b.com"); + } +} diff --git a/server/sonar-server/src/test/java/org/sonar/server/user/DeprecatedUserFinderTest.java b/server/sonar-server/src/test/java/org/sonar/server/user/DeprecatedUserFinderTest.java new file mode 100644 index 00000000000..02e1419375f --- /dev/null +++ b/server/sonar-server/src/test/java/org/sonar/server/user/DeprecatedUserFinderTest.java @@ -0,0 +1,84 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.server.user; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.sonar.api.database.model.User; +import org.sonar.api.utils.System2; +import org.sonar.db.DbTester; +import org.sonar.db.user.UserDao; + +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.nullValue; +import static org.junit.Assert.assertThat; +import static org.mockito.Mockito.mock; + + +public class DeprecatedUserFinderTest { + + @Rule + public DbTester dbTester = DbTester.create(System2.INSTANCE); + + @Before + public void init() { + dbTester.prepareDbUnit(DeprecatedUserFinderTest.class, "fixture.xml"); + } + + @Test + public void shouldFindUserByLogin() { + DeprecatedUserFinder finder = new DeprecatedUserFinder(new UserDao(dbTester.myBatis(), mock(System2.class))); + User user = finder.findByLogin("simon"); + assertThat(user.getId(), is(1)); + assertThat(user.getLogin(), is("simon")); + assertThat(user.getName(), is("Simon Brandhof")); + assertThat(user.getEmail(), is("simon.brandhof@sonarsource.com")); + + user = finder.findByLogin("godin"); + assertThat(user.getId(), is(2)); + assertThat(user.getLogin(), is("godin")); + assertThat(user.getName(), is("Evgeny Mandrikov")); + assertThat(user.getEmail(), is("evgeny.mandrikov@sonarsource.com")); + + user = finder.findByLogin("user"); + assertThat(user, nullValue()); + } + + @Test + public void shouldFindUserById() { + DeprecatedUserFinder finder = new DeprecatedUserFinder(new UserDao(dbTester.myBatis(), mock(System2.class))); + User user = finder.findById(1); + assertThat(user.getId(), is(1)); + assertThat(user.getLogin(), is("simon")); + assertThat(user.getName(), is("Simon Brandhof")); + assertThat(user.getEmail(), is("simon.brandhof@sonarsource.com")); + + user = finder.findById(2); + assertThat(user.getId(), is(2)); + assertThat(user.getLogin(), is("godin")); + assertThat(user.getName(), is("Evgeny Mandrikov")); + assertThat(user.getEmail(), is("evgeny.mandrikov@sonarsource.com")); + + user = finder.findById(3); + assertThat(user, nullValue()); + } + +} diff --git a/server/sonar-server/src/test/java/org/sonar/server/user/GroupMembershipFinderTest.java b/server/sonar-server/src/test/java/org/sonar/server/user/GroupMembershipFinderTest.java index a786ccf32ae..9a15c64e648 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/user/GroupMembershipFinderTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/user/GroupMembershipFinderTest.java @@ -22,7 +22,6 @@ package org.sonar.server.user; import org.junit.Before; import org.junit.Test; import org.mockito.ArgumentCaptor; -import org.sonar.core.user.GroupMembership; import org.sonar.db.user.GroupMembershipDao; import org.sonar.db.user.GroupMembershipDto; import org.sonar.db.user.GroupMembershipQuery; diff --git a/server/sonar-server/src/test/java/org/sonar/server/user/GroupMembershipServiceTest.java b/server/sonar-server/src/test/java/org/sonar/server/user/GroupMembershipServiceTest.java index c62315399b1..b89c6cc7936 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/user/GroupMembershipServiceTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/user/GroupMembershipServiceTest.java @@ -24,7 +24,6 @@ import java.util.List; import org.junit.Rule; import org.junit.Test; import org.sonar.api.utils.System2; -import org.sonar.core.user.GroupMembership; import org.sonar.db.DbTester; import org.sonar.server.exceptions.NotFoundException; diff --git a/server/sonar-server/src/test/java/org/sonar/server/user/GroupMembershipTest.java b/server/sonar-server/src/test/java/org/sonar/server/user/GroupMembershipTest.java new file mode 100644 index 00000000000..6f5e10a111f --- /dev/null +++ b/server/sonar-server/src/test/java/org/sonar/server/user/GroupMembershipTest.java @@ -0,0 +1,52 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.server.user; + +import org.junit.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +public class GroupMembershipTest { + + @Test + public void test_setters_and_getters() throws Exception { + GroupMembership group = new GroupMembership() + .setId(1L) + .setName("users") + .setMember(true); + + assertThat(group.id()).isEqualTo(1L); + assertThat(group.name()).isEqualTo("users"); + assertThat(group.isMember()).isTrue(); + } + + @Test + public void test_equals() throws Exception { + assertThat(new GroupMembership().setName("users")).isEqualTo(new GroupMembership().setName("users")); + assertThat(new GroupMembership().setName("users")).isNotEqualTo(new GroupMembership().setName("reviewers")); + + GroupMembership group = new GroupMembership() + .setId(1L) + .setName("users") + .setMember(true); + assertThat(group).isEqualTo(group); + } + +} diff --git a/server/sonar-server/src/test/resources/org/sonar/server/user/DeprecatedUserFinderTest/fixture.xml b/server/sonar-server/src/test/resources/org/sonar/server/user/DeprecatedUserFinderTest/fixture.xml new file mode 100644 index 00000000000..9d370e86a70 --- /dev/null +++ b/server/sonar-server/src/test/resources/org/sonar/server/user/DeprecatedUserFinderTest/fixture.xml @@ -0,0 +1,6 @@ +<dataset> + + <users id="1" login="simon" name="Simon Brandhof" email="simon.brandhof@sonarsource.com"/> + <users id="2" login="godin" name="Evgeny Mandrikov" email="evgeny.mandrikov@sonarsource.com"/> + +</dataset> |