Skip to content

Commit

Permalink
Read line hashes in streaming
Browse files Browse the repository at this point in the history
  • Loading branch information
julienlancelot committed Jan 21, 2015
1 parent ffb5468 commit 4d869a9
Show file tree
Hide file tree
Showing 11 changed files with 190 additions and 116 deletions.
Expand Up @@ -72,13 +72,13 @@ public void handle(Request request, Response response) throws Exception {
UserSession.get().checkGlobalPermission(GlobalPermissions.PREVIEW_EXECUTION); UserSession.get().checkGlobalPermission(GlobalPermissions.PREVIEW_EXECUTION);
final String moduleKey = request.mandatoryParam(PARAM_KEY); final String moduleKey = request.mandatoryParam(PARAM_KEY);


response.stream().setMediaType(MimeTypes.JSON);
PreviousIssueHelper previousIssueHelper = PreviousIssueHelper.create(new OutputStreamWriter(response.stream().output(), Charsets.UTF_8)); PreviousIssueHelper previousIssueHelper = PreviousIssueHelper.create(new OutputStreamWriter(response.stream().output(), Charsets.UTF_8));
DbSession session = dbClient.openSession(false); DbSession session = dbClient.openSession(false);
try { try {
ComponentDto moduleOrProject = dbClient.componentDao().getByKey(session, moduleKey); ComponentDto moduleOrProject = dbClient.componentDao().getByKey(session, moduleKey);
UserSession.get().checkComponentPermission(UserRole.USER, moduleKey); UserSession.get().checkComponentPermission(UserRole.USER, moduleKey);


response.stream().setMediaType(MimeTypes.JSON);
BatchIssueResultHandler batchIssueResultHandler = new BatchIssueResultHandler(previousIssueHelper); BatchIssueResultHandler batchIssueResultHandler = new BatchIssueResultHandler(previousIssueHelper);
if (moduleOrProject.isRootProject()) { if (moduleOrProject.isRootProject()) {
dbClient.issueDao().selectNonClosedIssuesByProjectUuid(session, moduleOrProject.uuid(), batchIssueResultHandler); dbClient.issueDao().selectNonClosedIssuesByProjectUuid(session, moduleOrProject.uuid(), batchIssueResultHandler);
Expand Down
Expand Up @@ -19,6 +19,8 @@
*/ */
package org.sonar.server.source.ws; package org.sonar.server.source.ws;


import com.google.common.base.Function;
import com.google.common.io.CharStreams;
import com.google.common.io.Resources; import com.google.common.io.Resources;
import org.apache.commons.io.Charsets; import org.apache.commons.io.Charsets;
import org.sonar.api.server.ws.Request; import org.sonar.api.server.ws.Request;
Expand All @@ -31,7 +33,9 @@
import org.sonar.server.db.DbClient; import org.sonar.server.db.DbClient;
import org.sonar.server.user.UserSession; import org.sonar.server.user.UserSession;


import java.io.OutputStream; import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.Reader;


public class HashAction implements RequestHandler { public class HashAction implements RequestHandler {


Expand Down Expand Up @@ -59,17 +63,49 @@ void define(WebService.NewController controller) {
@Override @Override
public void handle(Request request, Response response) throws Exception { public void handle(Request request, Response response) throws Exception {
try (DbSession session = dbClient.openSession(false)) { try (DbSession session = dbClient.openSession(false)) {
String componentKey = request.mandatoryParam("key"); final String componentKey = request.mandatoryParam("key");
UserSession.get().checkComponentPermission(UserRole.CODEVIEWER, componentKey); UserSession.get().checkComponentPermission(UserRole.CODEVIEWER, componentKey);
ComponentDto component = dbClient.componentDao().getByKey(session, componentKey); final ComponentDto component = dbClient.componentDao().getByKey(session, componentKey);
String lineHashes = dbClient.fileSourceDao().selectLineHashes(component.uuid(), session);
if (lineHashes == null) { response.stream().setMediaType("text/plain");
response.noContent(); OutputStreamWriter writer = new OutputStreamWriter(response.stream().output(), Charsets.UTF_8);
} else { try {
try (OutputStream output = response.stream().setMediaType("text/plain").output()) { HashFunction hashFunction = new HashFunction(writer, componentKey);
output.write(lineHashes.getBytes(Charsets.UTF_8)); dbClient.fileSourceDao().readLineHashesStream(session, component.uuid(), hashFunction);
if (!hashFunction.hasData()) {
response.noContent();
} }
} finally {
writer.close();
}
}
}

private class HashFunction implements Function<Reader, Void> {

private final OutputStreamWriter writer;
private final String componentKey;
private boolean hasData = false;

public HashFunction(OutputStreamWriter writer, String componentKey) {
this.writer = writer;
this.componentKey = componentKey;
}

@Override
public Void apply(Reader input) {
try {
hasData = true;
CharStreams.copy(input, writer);
} catch (IOException e) {
throw new IllegalStateException(String.format("Can't read line hashes of file '%s'", componentKey));
} }
return null;
}

public boolean hasData() {
return hasData;
} }
} }

} }
Expand Up @@ -62,11 +62,6 @@ public UserDoc getNullableByLogin(String login) {
return userIndex.getNullableByLogin(login); return userIndex.getNullableByLogin(login);
} }


@CheckForNull
public UserDoc getNullableByScmAccount(String scmAccount) {
return userIndex.getNullableByScmAccount(scmAccount);
}

public void index() { public void index() {
userIndexer.index(); userIndexer.index();
} }
Expand Down
Expand Up @@ -40,7 +40,6 @@
import org.sonar.server.db.DbClient; import org.sonar.server.db.DbClient;
import org.sonar.server.exceptions.BadRequestException; import org.sonar.server.exceptions.BadRequestException;
import org.sonar.server.exceptions.Message; import org.sonar.server.exceptions.Message;
import org.sonar.server.exceptions.NotFoundException;
import org.sonar.server.user.db.UserGroupDao; import org.sonar.server.user.db.UserGroupDao;
import org.sonar.server.util.Validation; import org.sonar.server.util.Validation;


Expand Down Expand Up @@ -116,13 +115,9 @@ public boolean create(NewUser newUser) {
public void update(UpdateUser updateUser) { public void update(UpdateUser updateUser) {
DbSession dbSession = dbClient.openSession(false); DbSession dbSession = dbClient.openSession(false);
try { try {
UserDto user = dbClient.userDao().selectNullableByLogin(dbSession, updateUser.login()); UserDto user = dbClient.userDao().selectByLogin(dbSession, updateUser.login());
if (user != null) { updateUserDto(dbSession, updateUser, user);
updateUserDto(dbSession, updateUser, user); updateUser(dbSession, user);
updateUser(dbSession, user);
} else {
throw new NotFoundException(String.format("User '%s' does not exists", updateUser.login()));
}
dbSession.commit(); dbSession.commit();
notifyNewUser(user.getLogin(), user.getName(), user.getEmail()); notifyNewUser(user.getLogin(), user.getName(), user.getEmail());
} finally { } finally {
Expand Down
Expand Up @@ -19,14 +19,15 @@
*/ */
package org.sonar.server.source.ws; package org.sonar.server.source.ws;


import org.junit.After;
import org.junit.Before; import org.junit.Before;
import org.junit.Rule;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner; import org.mockito.runners.MockitoJUnitRunner;
import org.sonar.api.web.UserRole; import org.sonar.api.web.UserRole;
import org.sonar.core.component.ComponentDto;
import org.sonar.core.persistence.DbSession; import org.sonar.core.persistence.DbSession;
import org.sonar.core.persistence.DbTester;
import org.sonar.core.source.db.FileSourceDao; import org.sonar.core.source.db.FileSourceDao;
import org.sonar.server.component.db.ComponentDao; import org.sonar.server.component.db.ComponentDao;
import org.sonar.server.db.DbClient; import org.sonar.server.db.DbClient;
Expand All @@ -37,30 +38,27 @@


import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.Assert.fail; import static org.junit.Assert.fail;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;


@RunWith(MockitoJUnitRunner.class) @RunWith(MockitoJUnitRunner.class)
public class HashActionTest { public class HashActionTest {


@Mock final static String COMPONENT_KEY = "Action.java";
DbClient dbClient; final static String PROJECT_KEY = "struts";


@Mock @Rule
ComponentDao componentDao; public DbTester db = new DbTester();


@Mock DbSession session;
FileSourceDao fileSourceDao;


WsTester tester; WsTester tester;


@Before @Before
public void setUp() throws Exception { public void before() throws Exception {
when(dbClient.componentDao()).thenReturn(componentDao); this.session = db.myBatis().openSession(false);
when(dbClient.fileSourceDao()).thenReturn(fileSourceDao);
when(dbClient.openSession(false)).thenReturn(mock(DbSession.class)); DbClient dbClient = new DbClient(db.database(), db.myBatis(), new FileSourceDao(db.myBatis()), new ComponentDao());

tester = new WsTester( tester = new WsTester(
new SourcesWs( new SourcesWs(
mock(ShowAction.class), mock(ShowAction.class),
Expand All @@ -73,39 +71,34 @@ public void setUp() throws Exception {
); );
} }


@After
public void after() {
this.session.close();
}

@Test @Test
public void show_hashes() throws Exception { public void show_hashes() throws Exception {
String componentKey = "project:src/File.xoo"; db.prepareDbUnit(getClass(), "shared.xml");
String uuid = "polop"; MockUserSession.set().setLogin("polop").addComponentPermission(UserRole.CODEVIEWER, PROJECT_KEY, COMPONENT_KEY);
ComponentDto component = new ComponentDto().setUuid(uuid);
String hashes = "polop\n" WsTester.TestRequest request = tester.newGetRequest("api/sources", "hash").setParam("key", COMPONENT_KEY);
+ "\n" assertThat(request.execute().outputAsString()).isEqualTo("987654");
+ "pilip";
MockUserSession.set().setLogin("polop").addComponentPermission(UserRole.CODEVIEWER, "palap", componentKey);
when(componentDao.getByKey(any(DbSession.class), eq(componentKey))).thenReturn(component);
when(fileSourceDao.selectLineHashes(eq(uuid), any(DbSession.class))).thenReturn(hashes);
WsTester.TestRequest request = tester.newGetRequest("api/sources", "hash").setParam("key", componentKey);
assertThat(request.execute().outputAsString()).isEqualTo(hashes);
} }


@Test @Test
public void hashes_empty_if_no_source() throws Exception { public void hashes_empty_if_no_source() throws Exception {
String componentKey = "project:src/File.xoo"; db.prepareDbUnit(getClass(), "no_source.xml");
String uuid = "polop"; MockUserSession.set().setLogin("polop").addComponentPermission(UserRole.CODEVIEWER, PROJECT_KEY, COMPONENT_KEY);
ComponentDto component = new ComponentDto().setUuid(uuid);
MockUserSession.set().setLogin("polop").addComponentPermission(UserRole.CODEVIEWER, "palap", componentKey); WsTester.TestRequest request = tester.newGetRequest("api/sources", "hash").setParam("key", COMPONENT_KEY);
when(componentDao.getByKey(any(DbSession.class), eq(componentKey))).thenReturn(component);
WsTester.TestRequest request = tester.newGetRequest("api/sources", "hash").setParam("key", componentKey);
request.execute().assertNoContent(); request.execute().assertNoContent();
} }


@Test @Test
public void fail_to_show_hashes_if_file_does_not_exist() throws Exception { public void fail_to_show_hashes_if_file_does_not_exist() throws Exception {
String componentKey = "project:src/File.xoo"; MockUserSession.set().setLogin("polop").addComponentPermission(UserRole.CODEVIEWER, PROJECT_KEY, COMPONENT_KEY);
MockUserSession.set().setLogin("polop").addComponentPermission(UserRole.CODEVIEWER, "palap", componentKey);
when(componentDao.getByKey(any(DbSession.class), eq(componentKey))).thenThrow(NotFoundException.class);
try { try {
WsTester.TestRequest request = tester.newGetRequest("api/sources", "hash").setParam("key", componentKey); WsTester.TestRequest request = tester.newGetRequest("api/sources", "hash").setParam("key", COMPONENT_KEY);
request.execute(); request.execute();
fail(); fail();
} catch (Exception e) { } catch (Exception e) {
Expand All @@ -115,11 +108,9 @@ public void fail_to_show_hashes_if_file_does_not_exist() throws Exception {


@Test(expected = ForbiddenException.class) @Test(expected = ForbiddenException.class)
public void fail_on_missing_permission() throws Exception { public void fail_on_missing_permission() throws Exception {
String componentKey = "project:src/File.xoo"; db.prepareDbUnit(getClass(), "shared.xml");
String uuid = "polop";
ComponentDto component = new ComponentDto().setUuid(uuid);
MockUserSession.set().setLogin("polop"); MockUserSession.set().setLogin("polop");
when(componentDao.getByKey(any(DbSession.class), eq(componentKey))).thenReturn(component); tester.newGetRequest("api/sources", "hash").setParam("key", COMPONENT_KEY).execute();
tester.newGetRequest("api/sources", "hash").setParam("key", componentKey).execute();
} }
} }
Expand Up @@ -167,13 +167,6 @@ public void get_by_login() throws Exception {
assertThat(service.getByLogin("user")).isNotNull(); assertThat(service.getByLogin("user")).isNotNull();
} }


@Test
public void get_nullable_by_scm_account() throws Exception {
createSampleUser();

assertThat(service.getNullableByScmAccount("u1")).isNotNull();
}

@Test @Test
public void index() throws Exception { public void index() throws Exception {
UserDto userDto = new UserDto().setLogin("user").setEmail("user@mail.com").setCreatedAt(System.currentTimeMillis()).setUpdatedAt(System.currentTimeMillis()); UserDto userDto = new UserDto().setLogin("user").setEmail("user@mail.com").setCreatedAt(System.currentTimeMillis()).setUpdatedAt(System.currentTimeMillis());
Expand Down
Expand Up @@ -44,7 +44,6 @@
import org.sonar.server.db.DbClient; import org.sonar.server.db.DbClient;
import org.sonar.server.exceptions.BadRequestException; import org.sonar.server.exceptions.BadRequestException;
import org.sonar.server.exceptions.Message; import org.sonar.server.exceptions.Message;
import org.sonar.server.exceptions.NotFoundException;
import org.sonar.server.user.db.GroupDao; import org.sonar.server.user.db.GroupDao;
import org.sonar.server.user.db.UserDao; import org.sonar.server.user.db.UserDao;
import org.sonar.server.user.db.UserGroupDao; import org.sonar.server.user.db.UserGroupDao;
Expand Down Expand Up @@ -743,21 +742,6 @@ public void not_associate_default_group_when_updating_user_if_already_existing()
assertThat(membership.groups().get(0).isMember()).isTrue(); assertThat(membership.groups().get(0).isMember()).isTrue();
} }


@Test
public void fail_to_update_unknown_user() throws Exception {
try {
userUpdater.update(UpdateUser.create("marius")
.setName("Marius2")
.setEmail("marius2@mail.com")
.setPassword("password2")
.setPasswordConfirmation("password2")
.setScmAccounts(newArrayList("ma2")));
fail();
} catch (Exception e) {
assertThat(e).isInstanceOf(NotFoundException.class).hasMessage("User 'marius' does not exists");
}
}

@Test @Test
public void fail_to_update_user_when_scm_account_is_already_used() throws Exception { public void fail_to_update_user_when_scm_account_is_already_used() throws Exception {
db.prepareDbUnit(getClass(), "fail_to_update_user_when_scm_account_is_already_used.xml"); db.prepareDbUnit(getClass(), "fail_to_update_user_when_scm_account_is_already_used.xml");
Expand Down
@@ -0,0 +1,7 @@
<dataset>

<projects id="100" kee="struts" root_id="[null]" qualifier="TRK" scope="PRJ" uuid="ABCD" project_uuid="ABCD" module_uuid="[null]" module_uuid_path="." path="[null]"/>
<projects id="101" kee="Action.java" root_id="100" qualifier="CLA" scope="PRJ" uuid="CDEF" project_uuid="ABCD" module_uuid="ABCD" module_uuid_path=".ABCD."
path="src/main/java/Action.java"/>

</dataset>
@@ -0,0 +1,13 @@
<dataset>

<projects id="100" kee="struts" root_id="[null]" qualifier="TRK" scope="PRJ" uuid="ABCD" project_uuid="ABCD" module_uuid="[null]" module_uuid_path="." path="[null]"/>
<projects id="101" kee="Action.java" root_id="100" qualifier="CLA" scope="PRJ" uuid="CDEF" project_uuid="ABCD" module_uuid="ABCD" module_uuid_path=".ABCD."
path="src/main/java/Action.java"/>

<file_sources id="101" project_uuid="ABCD" file_uuid="CDEF"
data="aef12a,alice,2014-04-25T12:34:56+0100,,class Foo" data_hash="hash"
line_hashes="987654"
src_hash="12345"
created_at="1414597442000" updated_at="1414683842000"/>

</dataset>
Expand Up @@ -58,27 +58,17 @@ public FileSourceDto select(String fileUuid) {


public <T> void readDataStream(String fileUuid, Function<Reader, T> function) { public <T> void readDataStream(String fileUuid, Function<Reader, T> function) {
DbSession dbSession = mybatis.openSession(false); DbSession dbSession = mybatis.openSession(false);
Connection connection = dbSession.getConnection();
PreparedStatement pstmt = null;
ResultSet rs = null;
Reader reader = null;
try { try {
pstmt = connection.prepareStatement("select data from file_sources where file_uuid = ?"); readColumnStream(dbSession, fileUuid, function, "data");
pstmt.setString(1, fileUuid);
rs = pstmt.executeQuery();
if (rs.next()) {
reader = rs.getCharacterStream(1);
function.apply(reader);
}
} catch (SQLException e) {
throw new IllegalStateException("Fail to read FILE_SOURCES.DATA of file " + fileUuid, e);
} finally { } finally {
IOUtils.closeQuietly(reader);
DbUtils.closeQuietly(connection, pstmt, rs);
MyBatis.closeQuietly(dbSession); MyBatis.closeQuietly(dbSession);
} }
} }


public <T> void readLineHashesStream(DbSession dbSession, String fileUuid, Function<Reader, T> function) {
readColumnStream(dbSession, fileUuid, function, "line_hashes");
}

public void insert(FileSourceDto dto) { public void insert(FileSourceDto dto) {
DbSession session = mybatis.openSession(false); DbSession session = mybatis.openSession(false);
try { try {
Expand All @@ -99,8 +89,24 @@ public void update(FileSourceDto dto) {
} }
} }


@CheckForNull private <T> void readColumnStream(DbSession dbSession, String fileUuid, Function<Reader, T> function, String column) {
public String selectLineHashes(String fileUuid, DbSession session) { Connection connection = dbSession.getConnection();
return session.getMapper(FileSourceMapper.class).selectLineHashes(fileUuid); PreparedStatement pstmt = null;
ResultSet rs = null;
Reader reader = null;
try {
pstmt = connection.prepareStatement("SELECT " + column + " FROM file_sources WHERE file_uuid = ?");
pstmt.setString(1, fileUuid);
rs = pstmt.executeQuery();
if (rs.next()) {
reader = rs.getCharacterStream(1);
function.apply(reader);
}
} catch (SQLException e) {
throw new IllegalStateException("Fail to read FILE_SOURCES." + column.toUpperCase() + " of file " + fileUuid, e);
} finally {
IOUtils.closeQuietly(reader);
DbUtils.closeQuietly(connection, pstmt, rs);
}
} }
} }

0 comments on commit 4d869a9

Please sign in to comment.