Created method to extract all currently online members of a group
additionally, refactored every ".Id" to ".ID"
This commit is contained in:
		| @@ -1,15 +1,14 @@ | ||||
| package envoy.server.net; | ||||
|  | ||||
| import java.util.Date; | ||||
| import java.util.HashMap; | ||||
| import java.util.HashSet; | ||||
| import java.util.Map; | ||||
| import java.util.Set; | ||||
| import java.util.*; | ||||
| import java.util.stream.Collectors; | ||||
|  | ||||
| import com.jenkov.nioserver.ISocketIdListener; | ||||
|  | ||||
| import envoy.data.User.UserStatus; | ||||
| import envoy.server.data.Group; | ||||
| import envoy.server.data.PersistenceManager; | ||||
| import envoy.server.data.User; | ||||
| import envoy.server.processors.UserStatusChangeProcessor; | ||||
|  | ||||
| /** | ||||
| @@ -48,60 +47,74 @@ public class ConnectionManager implements ISocketIdListener { | ||||
| 	public static ConnectionManager getInstance() { return connectionManager; } | ||||
|  | ||||
| 	@Override | ||||
| 	public void socketCancelled(long socketId) { | ||||
| 		if (!pendingSockets.remove(socketId)) { | ||||
| 	public void socketCancelled(long socketID) { | ||||
| 		if (!pendingSockets.remove(socketID)) { | ||||
| 			// Notify contacts of this users offline-going | ||||
| 			envoy.server.data.User user = PersistenceManager.getInstance().getUserById(getUserIdBySocketId(socketId)); | ||||
| 			envoy.server.data.User user = PersistenceManager.getInstance().getUserById(getUserIdBySocketId(socketID)); | ||||
| 			user.setStatus(UserStatus.OFFLINE); | ||||
| 			user.setLastSeen(new Date()); | ||||
| 			UserStatusChangeProcessor.updateUserStatus(user); | ||||
|  | ||||
| 			// Remove the socket | ||||
| 			sockets.entrySet().removeIf(e -> e.getValue() == socketId); | ||||
| 			sockets.entrySet().removeIf(e -> e.getValue() == socketID); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	@Override | ||||
| 	public void socketRegistered(long socketId) { pendingSockets.add(socketId); } | ||||
| 	public void socketRegistered(long socketID) { pendingSockets.add(socketID); } | ||||
|  | ||||
| 	/** | ||||
| 	 * Associates a socket ID with a user ID. | ||||
| 	 * | ||||
| 	 * @param userId   the user ID | ||||
| 	 * @param socketId the socket ID | ||||
| 	 * @param userID   the user ID | ||||
| 	 * @param socketID the socket ID | ||||
| 	 * @since Envoy Server Standalone v0.1-alpha | ||||
| 	 */ | ||||
| 	public void registerUser(long userId, long socketId) { | ||||
| 		sockets.put(userId, socketId); | ||||
| 		pendingSockets.remove(socketId); | ||||
| 	public void registerUser(long userID, long socketID) { | ||||
| 		sockets.put(userID, socketID); | ||||
| 		pendingSockets.remove(socketID); | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * @param userId the ID of the user registered at a socket | ||||
| 	 * @param userID the ID of the user registered at a socket | ||||
| 	 * @return the ID of the socket | ||||
| 	 * @since Envoy Server Standalone v0.1-alpha | ||||
| 	 */ | ||||
| 	public long getSocketId(long userId) { return sockets.get(userId); } | ||||
| 	public long getSocketId(long userID) { return sockets.get(userID); } | ||||
|  | ||||
| 	/** | ||||
| 	 * @param socketId the id of the socket whose User is needed | ||||
| 	 * @param socketID the id of the socket whose User is needed | ||||
| 	 * @return the userId associated with this socketId | ||||
| 	 * @since Envoy Server Standalone v0.1-alpha | ||||
| 	 */ | ||||
| 	public long getUserIdBySocketId(long socketId) { | ||||
| 		return sockets.entrySet().stream().filter(entry -> entry.getValue().equals(socketId)).findFirst().get().getKey(); | ||||
| 	public long getUserIdBySocketId(long socketID) { | ||||
| 		return sockets.entrySet().stream().filter(entry -> entry.getValue().equals(socketID)).findFirst().get().getKey(); | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * @param userId the ID of the user to check for | ||||
| 	 * @param userID the ID of the user to check for | ||||
| 	 * @return {@code true} if the user is online | ||||
| 	 * @since Envoy Server Standalone v0.1-alpha | ||||
| 	 */ | ||||
| 	public boolean isOnline(long userId) { return sockets.containsKey(userId); } | ||||
| 	public boolean isOnline(long userID) { return sockets.containsKey(userID); } | ||||
|  | ||||
| 	/** | ||||
| 	 * @return the userId of all users who are currently online | ||||
| 	 * @return the userIDs of all users who are currently online | ||||
| 	 * @since Envoy Server Standalone v0.1-alpha | ||||
| 	 */ | ||||
| 	public Set<Long> getOnlineUsers() { return sockets.keySet(); } | ||||
|  | ||||
| 	/** | ||||
| 	 * Returns all members of a group who are currently online. | ||||
| 	 * | ||||
| 	 * @param group the group to search for | ||||
| 	 * @return a set of all IDs of currently active members in this group | ||||
| 	 * @since Envoy Server Standalone v0.1-beta | ||||
| 	 */ | ||||
| 	public Set<Long> getOnlineUsersOfGroup(Group group) { | ||||
| 		Set<Long>	onlineMembers	= new HashSet<>(); | ||||
| 		Set<Long>	members			= group.getMembers().stream().map(User::getID).collect(Collectors.toSet()); | ||||
| 		members.forEach(userID -> { if (isOnline(userID)) onlineMembers.add(userID); }); | ||||
| 		return onlineMembers; | ||||
| 	} | ||||
| } | ||||
|   | ||||
| @@ -30,15 +30,15 @@ public class ObjectWriteProxy { | ||||
| 	public ObjectWriteProxy(WriteProxy writeProxy) { this.writeProxy = writeProxy; } | ||||
|  | ||||
| 	/** | ||||
| 	 * @param recipientSocketId the socket id of the recipient | ||||
| 	 * @param recipientSocketID the socket id of the recipient | ||||
| 	 * @param obj               the object to return to the client | ||||
| 	 * @throws IOException if the serialization of the object failed | ||||
| 	 * @since Envoy Server Standalone v0.1-alpha | ||||
| 	 */ | ||||
| 	public void write(long recipientSocketId, Object obj) throws IOException { | ||||
| 	public void write(long recipientSocketID, Object obj) throws IOException { | ||||
| 		// Create message targeted at the client | ||||
| 		Message response = writeProxy.getMessage(); | ||||
| 		response.socketId = recipientSocketId; | ||||
| 		response.socketId = recipientSocketID; | ||||
|  | ||||
| 		// Serialize object to byte array | ||||
| 		byte[] objBytes = SerializationUtils.writeToByteArray(obj); | ||||
|   | ||||
| @@ -25,15 +25,15 @@ public class ContactOperationProcessor implements ObjectProcessor<ContactOperati | ||||
| 	public void process(ContactOperationEvent evt, long socketId, ObjectWriteProxy writeProxy) throws IOException { | ||||
| 		switch (evt.getOperationType()) { | ||||
| 			case ADD: | ||||
| 				final long userId = ConnectionManager.getInstance().getUserIdBySocketId(socketId); | ||||
| 				final long userID = ConnectionManager.getInstance().getUserIdBySocketId(socketId); | ||||
| 				final long contactId = evt.get().getID(); | ||||
|  | ||||
| 				System.out.printf("Adding user %s to the contact list of user %d.%n", evt.get(), userId); | ||||
| 				PersistenceManager.getInstance().addUserContact(userId, contactId); | ||||
| 				System.out.printf("Adding user %s to the contact list of user %d.%n", evt.get(), userID); | ||||
| 				PersistenceManager.getInstance().addUserContact(userID, contactId); | ||||
|  | ||||
| 				// Notify the contact if online | ||||
| 				if (ConnectionManager.getInstance().isOnline(contactId)) writeProxy.write(connectionManager.getSocketId(contactId), | ||||
| 						new Contacts(Arrays.asList(PersistenceManager.getInstance().getUserById(userId).toCommon()))); | ||||
| 						new Contacts(Arrays.asList(PersistenceManager.getInstance().getUserById(userID).toCommon()))); | ||||
| 				break; | ||||
| 			default: | ||||
| 				System.err.printf("Received %s with an unsupported operation.%n", evt); | ||||
|   | ||||
| @@ -30,10 +30,10 @@ public class ContactsRequestEventProcessor implements ObjectProcessor<ContactSea | ||||
| 	 * @since Envoy Server Standalone v0.1-alpha | ||||
| 	 */ | ||||
| 	@Override | ||||
| 	public void process(ContactSearchRequest request, long socketId, ObjectWriteProxy writeProxy) throws IOException { | ||||
| 		writeProxy.write(socketId, | ||||
| 	public void process(ContactSearchRequest request, long socketID, ObjectWriteProxy writeProxy) throws IOException { | ||||
| 		writeProxy.write(socketID, | ||||
| 				new ContactSearchResult(PersistenceManager.getInstance() | ||||
| 					.searchUsers(request.get(), ConnectionManager.getInstance().getUserIdBySocketId(socketId)) | ||||
| 					.searchUsers(request.get(), ConnectionManager.getInstance().getUserIdBySocketId(socketID)) | ||||
| 					.stream() | ||||
| 					.map(User::toCommon) | ||||
| 					.collect(Collectors.toList()))); | ||||
|   | ||||
| @@ -19,13 +19,13 @@ public class GroupCreationProcessor implements ObjectProcessor<GroupCreationEven | ||||
| 	private final PersistenceManager	persistenceManager	= PersistenceManager.getInstance(); | ||||
|  | ||||
| 	@Override | ||||
| 	public void process(GroupCreationEvent input, long socketId, ObjectWriteProxy writeProxy) throws IOException { | ||||
| 	public void process(GroupCreationEvent input, long socketID, ObjectWriteProxy writeProxy) throws IOException { | ||||
| 		envoy.server.data.Group group = new envoy.server.data.Group(); | ||||
| 		group.setName(input.get()); | ||||
| 		// TODO adjust event, so it sends a members list as well, which can be initially | ||||
| 		// set here | ||||
| 		persistenceManager.addContact(group); | ||||
| 		writeProxy.write(socketId, group); // TODO Prepare the client to receive the group object after sending the | ||||
| 		writeProxy.write(socketID, group); // TODO Prepare the client to receive the group object after sending the | ||||
| 											// groupCreationEvent to the server. | ||||
| 	} | ||||
|  | ||||
|   | ||||
| @@ -25,11 +25,11 @@ public class IDGeneratorRequestProcessor implements ObjectProcessor<IDGeneratorR | ||||
| 	public Class<IDGeneratorRequest> getInputClass() { return IDGeneratorRequest.class; } | ||||
|  | ||||
| 	@Override | ||||
| 	public void process(IDGeneratorRequest input, long socketId, ObjectWriteProxy writeProxy) throws IOException { | ||||
| 	public void process(IDGeneratorRequest input, long socketID, ObjectWriteProxy writeProxy) throws IOException { | ||||
| 		System.out.println("Received id generation request."); | ||||
| 		var generator = createIDGenerator(); | ||||
| 		System.out.println("Sending new id generator " + generator); | ||||
| 		writeProxy.write(socketId, generator); | ||||
| 		writeProxy.write(socketID, generator); | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| @@ -44,10 +44,10 @@ public class IDGeneratorRequestProcessor implements ObjectProcessor<IDGeneratorR | ||||
| 	 * @since Envoy Server Standalone v0.1-beta | ||||
| 	 */ | ||||
| 	public static IDGenerator createIDGenerator(long range) { | ||||
| 		ConfigItem	currentId	= PersistenceManager.getInstance().getConfigItemById("currentMessageId"); | ||||
| 		IDGenerator	generator	= new IDGenerator(Integer.parseInt(currentId.getValue()), range); | ||||
| 		currentId.setValue(String.valueOf(Integer.parseInt(currentId.getValue()) + range)); | ||||
| 		PersistenceManager.getInstance().updateConfigItem(currentId); | ||||
| 		ConfigItem	currentID	= PersistenceManager.getInstance().getConfigItemById("currentMessageId"); | ||||
| 		IDGenerator	generator	= new IDGenerator(Integer.parseInt(currentID.getValue()), range); | ||||
| 		currentID.setValue(String.valueOf(Integer.parseInt(currentID.getValue()) + range)); | ||||
| 		PersistenceManager.getInstance().updateConfigItem(currentID); | ||||
| 		return generator; | ||||
| 	} | ||||
| } | ||||
|   | ||||
| @@ -38,18 +38,18 @@ public class LoginCredentialProcessor implements ObjectProcessor<LoginCredential | ||||
| 	private final ConnectionManager		connectionManager	= ConnectionManager.getInstance(); | ||||
|  | ||||
| 	@Override | ||||
| 	public void process(LoginCredentials input, long socketId, ObjectWriteProxy writeProxy) throws IOException { | ||||
| 	public void process(LoginCredentials input, long socketID, ObjectWriteProxy writeProxy) throws IOException { | ||||
| 		UserStatusChangeProcessor.setWriteProxy(writeProxy); | ||||
| 		System.out.println(String.format("Received login credentials %s from socket ID %d", input, socketId)); | ||||
| 		System.out.println(String.format("Received login credentials %s from socket ID %d", input, socketID)); | ||||
|  | ||||
| 		envoy.server.data.User user = getUser(input, socketId, writeProxy); | ||||
| 		envoy.server.data.User user = getUser(input, socketID, writeProxy); | ||||
|  | ||||
| 		// Not logged in successfully | ||||
| 		if (user == null) { | ||||
| 			System.out.println("Rejecting handshake on socket " + socketId); | ||||
| 			System.out.println("Rejecting handshake on socket " + socketID); | ||||
| 			return; | ||||
| 		} | ||||
| 		connectionManager.registerUser(user.getID(), socketId); | ||||
| 		connectionManager.registerUser(user.getID(), socketID); | ||||
|  | ||||
| 		// Notifies contacts of this users online-going and updates his status in the | ||||
| 		// database | ||||
| @@ -62,14 +62,14 @@ public class LoginCredentialProcessor implements ObjectProcessor<LoginCredential | ||||
|  | ||||
| 		// Complete handshake | ||||
| 		System.out.println("Sending user..."); | ||||
| 		writeProxy.write(socketId, user.toCommon()); | ||||
| 		writeProxy.write(socketID, user.toCommon()); | ||||
| 		System.out.println("Sending contacts..."); | ||||
| 		writeProxy.write(socketId, contacts); | ||||
| 		writeProxy.write(socketID, contacts); | ||||
| 		System.out.println("Acquiring pending messages for the client..."); | ||||
| 		List<Message> pendingMessages = PersistenceManager.getInstance().getUnreadMessages(user); | ||||
| 		for (Message msg : pendingMessages) { | ||||
| 			System.out.println("Sending message " + msg.toCommonMessage()); | ||||
| 			writeProxy.write(socketId, msg.toCommonMessage()); | ||||
| 			writeProxy.write(socketID, msg.toCommonMessage()); | ||||
| 			msg.setReceivedDate(new Date()); | ||||
| 			msg.setStatus(MessageStatus.RECEIVED); | ||||
| 			PersistenceManager.getInstance().updateMessage(msg); | ||||
| @@ -79,13 +79,13 @@ public class LoginCredentialProcessor implements ObjectProcessor<LoginCredential | ||||
| 	@Override | ||||
| 	public Class<LoginCredentials> getInputClass() { return LoginCredentials.class; } | ||||
|  | ||||
| 	private envoy.server.data.User getUser(LoginCredentials credentials, long socketId, ObjectWriteProxy writeProxy) throws IOException { | ||||
| 		return credentials.isRegistration() ? newUser(credentials, socketId, writeProxy) : checkForExistingUser(credentials, socketId, writeProxy); | ||||
| 	private envoy.server.data.User getUser(LoginCredentials credentials, long socketID, ObjectWriteProxy writeProxy) throws IOException { | ||||
| 		return credentials.isRegistration() ? newUser(credentials, socketID, writeProxy) : checkForExistingUser(credentials, socketID, writeProxy); | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * @param credentials the input to evaluate | ||||
| 	 * @param socketId    the socket ID at which the client performing the handshake | ||||
| 	 * @param socketID    the socket ID at which the client performing the handshake | ||||
| 	 *                    is connected | ||||
| 	 * @param writeProxy  the {@link ObjectWriteProxy} to use if login was not | ||||
| 	 *                    successful | ||||
| @@ -93,34 +93,34 @@ public class LoginCredentialProcessor implements ObjectProcessor<LoginCredential | ||||
| 	 * @throws IOException if sending the failed login back to the client failed | ||||
| 	 * @since Envoy Server Standalone v0.1-alpha | ||||
| 	 */ | ||||
| 	private envoy.server.data.User checkForExistingUser(LoginCredentials credentials, long socketId, ObjectWriteProxy writeProxy) throws IOException { | ||||
| 	private envoy.server.data.User checkForExistingUser(LoginCredentials credentials, long socketID, ObjectWriteProxy writeProxy) throws IOException { | ||||
| 		try { | ||||
| 			envoy.server.data.User user = persistenceManager.getUserByName(credentials.getIdentifier()); | ||||
|  | ||||
| 			// Checking if user is already online | ||||
| 			if (connectionManager.isOnline(user.getID())) { | ||||
| 				writeProxy.write(socketId, new HandshakeRejectionEvent(HandshakeRejectionEvent.ALREADY_ONLINE)); | ||||
| 				writeProxy.write(socketID, new HandshakeRejectionEvent(HandshakeRejectionEvent.ALREADY_ONLINE)); | ||||
| 				return null; | ||||
| 			} | ||||
| 			// Evaluating the correctness of the password hash | ||||
| 			if (!Arrays.equals(credentials.getPasswordHash(), user.getPasswordHash())) { | ||||
| 				writeProxy.write(socketId, new HandshakeRejectionEvent(HandshakeRejectionEvent.WRONG_PASSWORD)); | ||||
| 				writeProxy.write(socketID, new HandshakeRejectionEvent(HandshakeRejectionEvent.WRONG_PASSWORD)); | ||||
| 				return null; | ||||
| 			} | ||||
| 			return user; | ||||
| 		} catch (NoResultException e) { | ||||
| 			// Checking if user exists | ||||
| 			writeProxy.write(socketId, new HandshakeRejectionEvent(HandshakeRejectionEvent.USER_DOES_NOT_EXIST)); | ||||
| 			writeProxy.write(socketID, new HandshakeRejectionEvent(HandshakeRejectionEvent.USER_DOES_NOT_EXIST)); | ||||
| 		} catch (InputMismatchException e) { | ||||
| 			// Checking if the given password hash is correct | ||||
| 			writeProxy.write(socketId, new HandshakeRejectionEvent(HandshakeRejectionEvent.WRONG_PASSWORD)); | ||||
| 			writeProxy.write(socketID, new HandshakeRejectionEvent(HandshakeRejectionEvent.WRONG_PASSWORD)); | ||||
| 		} | ||||
| 		return null; | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * @param credentials the credentials upon which to create the new {@link User} | ||||
| 	 * @param socketId    the socketID at which the client performing the handshake | ||||
| 	 * @param socketID    the socketID at which the client performing the handshake | ||||
| 	 *                    is connected | ||||
| 	 * @param writeProxy  the write proxy used to notify the client about handshake | ||||
| 	 *                    rejection | ||||
| @@ -128,12 +128,12 @@ public class LoginCredentialProcessor implements ObjectProcessor<LoginCredential | ||||
| 	 * @throws IOException if sending the failed login back to the client failed | ||||
| 	 * @since Envoy Server Standalone v0.1-alpha | ||||
| 	 */ | ||||
| 	private envoy.server.data.User newUser(LoginCredentials credentials, long socketId, ObjectWriteProxy writeProxy) throws IOException { | ||||
| 	private envoy.server.data.User newUser(LoginCredentials credentials, long socketID, ObjectWriteProxy writeProxy) throws IOException { | ||||
| 		try { | ||||
| 			// Checking that no user already has this identifier | ||||
| 			PersistenceManager.getInstance().getUserByName(credentials.getIdentifier()); | ||||
| 			// this code only gets executed if this user already exists | ||||
| 			writeProxy.write(socketId, new HandshakeRejectionEvent(HandshakeRejectionEvent.USER_EXISTS_ALREADY)); | ||||
| 			writeProxy.write(socketID, new HandshakeRejectionEvent(HandshakeRejectionEvent.USER_EXISTS_ALREADY)); | ||||
| 			return null; | ||||
| 		} catch (NoResultException e) { | ||||
| 			// Creation of a new user | ||||
|   | ||||
| @@ -26,7 +26,7 @@ import envoy.server.net.ObjectWriteProxy; | ||||
| public class MessageProcessor implements ObjectProcessor<Message> { | ||||
|  | ||||
| 	@Override | ||||
| 	public void process(Message message, long socketId, ObjectWriteProxy writeProxy) { | ||||
| 	public void process(Message message, long socketID, ObjectWriteProxy writeProxy) { | ||||
| 		message.nextStatus(); | ||||
| 		ConnectionManager	connectionManager	= ConnectionManager.getInstance(); | ||||
| 		Contact				recipient			= PersistenceManager.getInstance().getContactById(message.getID()); | ||||
| @@ -36,7 +36,7 @@ public class MessageProcessor implements ObjectProcessor<Message> { | ||||
| 			sendToUser(connectionManager, message, writeProxy); | ||||
| 			// Sending a messageStatusChangeEvent to the sender | ||||
| 			try { | ||||
| 				writeProxy.write(socketId, new MessageStatusChangeEvent(message)); | ||||
| 				writeProxy.write(socketID, new MessageStatusChangeEvent(message)); | ||||
| 			} catch (IOException e) { | ||||
| 				System.err.println("Could not send messageStatusChangeEvent to the sender of this message with ID: " + message.getID()); | ||||
| 				e.printStackTrace(); | ||||
|   | ||||
| @@ -23,7 +23,7 @@ public class MessageStatusChangeProcessor implements ObjectProcessor<MessageStat | ||||
| 	private final ConnectionManager		connectionManager	= ConnectionManager.getInstance(); | ||||
|  | ||||
| 	@Override | ||||
| 	public void process(MessageStatusChangeEvent input, long socketId, ObjectWriteProxy writeProxy) throws IOException { | ||||
| 	public void process(MessageStatusChangeEvent input, long socketID, ObjectWriteProxy writeProxy) throws IOException { | ||||
| 		// Any other status than READ is not supposed to be sent to the server | ||||
| 		if (input.get() != MessageStatus.READ) throw new IOException(new EnvoyException("Message " + input + " has an invalid status")); | ||||
|  | ||||
|   | ||||
| @@ -26,10 +26,10 @@ public interface ObjectProcessor<T> { | ||||
|  | ||||
| 	/** | ||||
| 	 * @param input      the request object | ||||
| 	 * @param socketId   the ID of the socket from which the object was received | ||||
| 	 * @param socketID   the ID of the socket from which the object was received | ||||
| 	 * @param writeProxy the object that allows writing to a client | ||||
| 	 * @throws IOException if something went wrong during processing | ||||
| 	 * @since Envoy Server Standalone v0.1-alpha | ||||
| 	 */ | ||||
| 	void process(T input, long socketId, ObjectWriteProxy writeProxy) throws IOException; | ||||
| 	void process(T input, long socketID, ObjectWriteProxy writeProxy) throws IOException; | ||||
| } | ||||
|   | ||||
| @@ -28,7 +28,7 @@ public class UserStatusChangeProcessor implements ObjectProcessor<UserStatusChan | ||||
| 	public Class<UserStatusChangeEvent> getInputClass() { return UserStatusChangeEvent.class; } | ||||
|  | ||||
| 	@Override | ||||
| 	public void process(UserStatusChangeEvent input, long socketId, ObjectWriteProxy writeProxy) throws IOException { | ||||
| 	public void process(UserStatusChangeEvent input, long socketID, ObjectWriteProxy writeProxy) throws IOException { | ||||
| 		// new status should not equal old status | ||||
| 		if (input.get().equals(persistenceManager.getUserById(input.getID()).getStatus())) { | ||||
| 			System.out.println("Received an unnecessary UserStatusChangeEvent"); | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 delvh
					delvh