-
Notifications
You must be signed in to change notification settings - Fork 48
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
14 changed files
with
1,567 additions
and
0 deletions.
There are no files selected for viewing
99 changes: 99 additions & 0 deletions
99
src/main/java/ru/vk/itmo/test/alenkovayulya/ServerImpl.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,99 @@ | ||
package ru.vk.itmo.test.alenkovayulya; | ||
|
||
import one.nio.http.*; | ||
import one.nio.server.AcceptorConfig; | ||
import ru.vk.itmo.ServiceConfig; | ||
import ru.vk.itmo.dao.BaseEntry; | ||
import ru.vk.itmo.dao.Entry; | ||
import ru.vk.itmo.test.alenkovayulya.dao.ReferenceDao; | ||
|
||
import java.io.IOException; | ||
import java.io.UncheckedIOException; | ||
import java.lang.foreign.MemorySegment; | ||
import java.lang.foreign.ValueLayout; | ||
import java.nio.charset.StandardCharsets; | ||
|
||
public class ServerImpl extends HttpServer { | ||
|
||
private final ReferenceDao referenceDao; | ||
|
||
public ServerImpl(ServiceConfig serviceConfig, ReferenceDao referenceDao) throws IOException { | ||
super(createServerConfig(serviceConfig)); | ||
this.referenceDao = referenceDao; | ||
} | ||
|
||
private static HttpServerConfig createServerConfig(ServiceConfig serviceConfig) { | ||
HttpServerConfig serverConfig = new HttpServerConfig(); | ||
AcceptorConfig acceptorConfig = new AcceptorConfig(); | ||
acceptorConfig.port = serviceConfig.selfPort(); | ||
acceptorConfig.reusePort = true; | ||
|
||
serverConfig.acceptors = new AcceptorConfig[]{acceptorConfig}; | ||
serverConfig.closeSessions = true; | ||
return serverConfig; | ||
} | ||
|
||
|
||
@Path("/v0/entity") | ||
@RequestMethod(Request.METHOD_GET) | ||
public Response getEntity(@Param(value = "id", required = true) String id) { | ||
if (isEmptyId(id)) { | ||
return new Response(Response.BAD_REQUEST, Response.EMPTY); | ||
} | ||
Entry<MemorySegment> value = referenceDao.get(convertBytesToMemorySegment(id.getBytes(StandardCharsets.UTF_8))); | ||
|
||
return value == null ? | ||
new Response(Response.NOT_FOUND, Response.EMPTY) : | ||
Response.ok(value.value().toArray(ValueLayout.JAVA_BYTE)); | ||
} | ||
|
||
@Path("/v0/entity") | ||
@RequestMethod(Request.METHOD_PUT) | ||
public Response putEntity(@Param(value = "id", required = true) String id, Request request) { | ||
if (isEmptyId(id)) { | ||
return new Response(Response.BAD_REQUEST, Response.EMPTY); | ||
} | ||
referenceDao.upsert(new BaseEntry<>( | ||
convertBytesToMemorySegment(id.getBytes(StandardCharsets.UTF_8)), | ||
convertBytesToMemorySegment(request.getBody()))); | ||
return new Response(Response.CREATED, Response.EMPTY); | ||
} | ||
|
||
@Path("/v0/entity") | ||
@RequestMethod(Request.METHOD_DELETE) | ||
public Response deleteEntity(@Param(value = "id", required = true) String id) { | ||
if (isEmptyId(id)) { | ||
return new Response(Response.BAD_REQUEST, Response.EMPTY); | ||
} | ||
referenceDao.upsert(new BaseEntry<>( | ||
convertBytesToMemorySegment(id.getBytes(StandardCharsets.UTF_8)), null)); | ||
return new Response(Response.ACCEPTED, Response.EMPTY); | ||
} | ||
|
||
|
||
@Override | ||
public void handleDefault(Request request, HttpSession session) throws IOException { | ||
switch (request.getMethodName()) { | ||
case "GET", "PUT", "DELETE" -> session.sendResponse(new Response(Response.BAD_REQUEST, Response.EMPTY)); | ||
default -> session.sendResponse(new Response(Response.METHOD_NOT_ALLOWED, Response.EMPTY)); | ||
} | ||
} | ||
|
||
@Override | ||
public synchronized void stop() { | ||
super.stop(); | ||
try { | ||
referenceDao.close(); | ||
} catch (IOException e) { | ||
throw new UncheckedIOException(e); | ||
} | ||
} | ||
|
||
private boolean isEmptyId(String id) { | ||
return id.isEmpty() && id.isBlank(); | ||
} | ||
|
||
private MemorySegment convertBytesToMemorySegment(byte[] byteArray) { | ||
return MemorySegment.ofArray(byteArray); | ||
} | ||
} |
26 changes: 26 additions & 0 deletions
26
src/main/java/ru/vk/itmo/test/alenkovayulya/ServerInitializer.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
package ru.vk.itmo.test.alenkovayulya; | ||
|
||
import ru.vk.itmo.ServiceConfig; | ||
import ru.vk.itmo.dao.Config; | ||
import ru.vk.itmo.test.alenkovayulya.dao.ReferenceDao; | ||
|
||
import java.io.IOException; | ||
import java.nio.file.Files; | ||
import java.util.List; | ||
|
||
public class ServerInitializer { | ||
public static final int PORT = 8080; | ||
public static final String URL = "http://localhost"; | ||
|
||
|
||
public static void main(String[] args) throws IOException { | ||
ServiceConfig config = new ServiceConfig(PORT, URL, List.of(URL), | ||
Files.createTempDirectory("reports") | ||
); | ||
|
||
ReferenceDao dao = new ReferenceDao(new Config(config.workingDir(), 1024)); | ||
ServerImpl server = new ServerImpl(config, dao); | ||
server.start(); | ||
} | ||
|
||
} |
44 changes: 44 additions & 0 deletions
44
src/main/java/ru/vk/itmo/test/alenkovayulya/ServiceImpl.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
package ru.vk.itmo.test.alenkovayulya; | ||
|
||
import ru.vk.itmo.Service; | ||
import ru.vk.itmo.ServiceConfig; | ||
import ru.vk.itmo.dao.Config; | ||
import ru.vk.itmo.test.ServiceFactory; | ||
import ru.vk.itmo.test.alenkovayulya.dao.ReferenceDao; | ||
|
||
import java.io.IOException; | ||
import java.util.concurrent.CompletableFuture; | ||
|
||
public class ServiceImpl implements Service { | ||
|
||
private ReferenceDao referenceDao; | ||
private ServerImpl server; | ||
private final ServiceConfig config; | ||
|
||
public ServiceImpl(ServiceConfig config) { | ||
this.config = config; | ||
|
||
} | ||
@Override | ||
public CompletableFuture<Void> start() throws IOException { | ||
referenceDao = new ReferenceDao(new Config(config.workingDir(), 1024)); | ||
server = new ServerImpl(config, referenceDao); | ||
server.start(); | ||
return CompletableFuture.completedFuture(null); | ||
} | ||
@Override | ||
public CompletableFuture<Void> stop() throws IOException { | ||
server.stop(); | ||
referenceDao.close(); | ||
return CompletableFuture.completedFuture(null); | ||
} | ||
|
||
@ServiceFactory(stage = 1) | ||
public static class Factory implements ServiceFactory.Factory { | ||
|
||
@Override | ||
public Service create(ServiceConfig config) { | ||
return new ServiceImpl(config); | ||
} | ||
} | ||
} |
48 changes: 48 additions & 0 deletions
48
src/main/java/ru/vk/itmo/test/alenkovayulya/dao/ByteArraySegment.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
package ru.vk.itmo.test.alenkovayulya.dao; | ||
|
||
import java.io.IOException; | ||
import java.lang.foreign.MemorySegment; | ||
import java.nio.ByteBuffer; | ||
|
||
/** | ||
* Growable buffer with {@link ByteBuffer} and {@link MemorySegment} interface. | ||
* | ||
* @author incubos | ||
*/ | ||
final class ByteArraySegment { | ||
private byte[] array; | ||
private MemorySegment segment; | ||
|
||
ByteArraySegment(final int capacity) { | ||
this.array = new byte[capacity]; | ||
this.segment = MemorySegment.ofArray(array); | ||
} | ||
|
||
void withArray(final ArrayConsumer consumer) throws IOException { | ||
consumer.process(array); | ||
} | ||
|
||
MemorySegment segment() { | ||
return segment; | ||
} | ||
|
||
void ensureCapacity(final long size) { | ||
if (size > Integer.MAX_VALUE) { | ||
throw new IllegalArgumentException("Too big!"); | ||
} | ||
|
||
final int capacity = (int) size; | ||
if (array.length >= capacity) { | ||
return; | ||
} | ||
|
||
// Grow to the nearest bigger power of 2 | ||
final int newSize = Integer.highestOneBit(capacity) << 1; | ||
array = new byte[newSize]; | ||
segment = MemorySegment.ofArray(array); | ||
} | ||
|
||
interface ArrayConsumer { | ||
void process(byte[] array) throws IOException; | ||
} | ||
} |
52 changes: 52 additions & 0 deletions
52
src/main/java/ru/vk/itmo/test/alenkovayulya/dao/LiveFilteringIterator.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
package ru.vk.itmo.test.alenkovayulya.dao; | ||
|
||
import ru.vk.itmo.dao.Entry; | ||
|
||
import java.lang.foreign.MemorySegment; | ||
import java.util.Iterator; | ||
import java.util.NoSuchElementException; | ||
|
||
/** | ||
* Filters non tombstone {@link Entry}s. | ||
* | ||
* @author incubos | ||
*/ | ||
final class LiveFilteringIterator implements Iterator<Entry<MemorySegment>> { | ||
private final Iterator<Entry<MemorySegment>> delegate; | ||
private Entry<MemorySegment> next; | ||
|
||
LiveFilteringIterator(final Iterator<Entry<MemorySegment>> delegate) { | ||
this.delegate = delegate; | ||
skipTombstones(); | ||
} | ||
|
||
private void skipTombstones() { | ||
while (delegate.hasNext()) { | ||
final Entry<MemorySegment> entry = delegate.next(); | ||
if (entry.value() != null) { | ||
this.next = entry; | ||
break; | ||
} | ||
} | ||
} | ||
|
||
@Override | ||
public boolean hasNext() { | ||
return next != null; | ||
} | ||
|
||
@Override | ||
public Entry<MemorySegment> next() { | ||
if (!hasNext()) { | ||
throw new NoSuchElementException(); | ||
} | ||
|
||
// Consume | ||
final Entry<MemorySegment> result = next; | ||
next = null; | ||
|
||
skipTombstones(); | ||
|
||
return result; | ||
} | ||
} |
49 changes: 49 additions & 0 deletions
49
src/main/java/ru/vk/itmo/test/alenkovayulya/dao/MemTable.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
package ru.vk.itmo.test.alenkovayulya.dao; | ||
|
||
import ru.vk.itmo.dao.Entry; | ||
|
||
import java.lang.foreign.MemorySegment; | ||
import java.util.Iterator; | ||
import java.util.NavigableMap; | ||
import java.util.concurrent.ConcurrentSkipListMap; | ||
|
||
/** | ||
* Memory table. | ||
* | ||
* @author incubos | ||
*/ | ||
final class MemTable { | ||
private final NavigableMap<MemorySegment, Entry<MemorySegment>> map = | ||
new ConcurrentSkipListMap<>( | ||
MemorySegmentComparator.INSTANCE); | ||
|
||
boolean isEmpty() { | ||
return map.isEmpty(); | ||
} | ||
|
||
Iterator<Entry<MemorySegment>> get( | ||
final MemorySegment from, | ||
final MemorySegment to) { | ||
if (from == null && to == null) { | ||
// All | ||
return map.values().iterator(); | ||
} else if (from == null) { | ||
// Head | ||
return map.headMap(to).values().iterator(); | ||
} else if (to == null) { | ||
// Tail | ||
return map.tailMap(from).values().iterator(); | ||
} else { | ||
// Slice | ||
return map.subMap(from, to).values().iterator(); | ||
} | ||
} | ||
|
||
Entry<MemorySegment> get(final MemorySegment key) { | ||
return map.get(key); | ||
} | ||
|
||
Entry<MemorySegment> upsert(final Entry<MemorySegment> entry) { | ||
return map.put(entry.key(), entry); | ||
} | ||
} |
Oops, something went wrong.