All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.atomix.resource.ResourceStateMachine Maven / Gradle / Ivy

There is a newer version: 1.0.8
Show newest version
/*
 * Copyright 2015 the original author or authors.
 *
 * Licensed 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 io.atomix.resource;

import io.atomix.catalyst.util.Assert;
import io.atomix.copycat.server.Commit;
import io.atomix.copycat.server.StateMachine;
import io.atomix.copycat.server.StateMachineExecutor;
import io.atomix.copycat.server.session.ServerSession;
import io.atomix.copycat.server.session.SessionListener;
import io.atomix.resource.internal.ResourceCommand;
import io.atomix.resource.internal.ResourceEvent;
import io.atomix.resource.internal.ResourceQuery;

import java.util.*;

/**
 * Base class for resource state machines.
 * 

* Resource implementations should extend the resource state machine for support in Atomix core. * This state machine implements functionality necessary to multiplexing the state machine on a * single Copycat log and provides facilities for configuring and cleaning up the resource upon * deletion. *

* To implement a resource state machine, simply extend this class. State machines must adhere * to the same rules as those outlined in the Copycat documentation. See the {@link StateMachine} * documentation for more information. *

 *   {@code
 *   public class MapStateMachine extends ResourceStateMachine {
 *     private final Map map = new HashMap<>();
 *
 *     public void put(Commit commit) {
 *       try {
 *         map.put(commit.operation().key(), commit.operation().value());
 *       } finally {
 *         commit.close();
 *       }
 *     }
 *   }
 *   }
 * 
* Resource state machines should override the {@link #delete()} method to perform cleanup of * the resource state upon deletion of the resource. Cleanup tasks might include notifying clients * of the deletion of the resource or releasing {@link Commit commits} held by the state machine. *
 *   {@code
 *   public class MapStateMachine extends ResourceStateMachine {
 *     private final Map> map = new HashMap<>();
 *
 *     public void put(Commit commit) {
 *       map.put(commit.operation().key(), commit);
 *     }
 *
 *     public void delete() {
 *       for (Map.Entry> entry : map.entrySet()) {
 *         entry.getValue().close();
 *       }
 *       map.clear();
 *     }
 *   }
 *   }
 * 
* Resource state machines implement Copycat's {@link SessionListener} interface to allow state * machines to listen for changes in client sessions. When used in an Atomix cluster, sessions * are specific to a state machine and not the entire cluster. When a client or replica opens * a resource, a new logical session to that resource is opened and the session listener's * {@link SessionListener#register(ServerSession)} method will be called. When a client or replica * closes a resource, the {@link SessionListener#close(ServerSession)} method will be called. * In other words, sessions in resource state machines represent a client's open resource instance, * and event messages {@link ServerSession#publish(String, Object) published} to that session will * be received by the specific client side {@link Resource} instance. *
 *   {@code
 *   public class MapStateMachine extends ResourceStateMachine {
 *     private final Map map = new HashMap<>();
 *     private final Set listeners = new HashSet<>();
 *
 *     public void listen(Commit commit) {
 *       listeners.add(commit.session());
 *     }
 *
 *     public void put(Commit commit) {
 *       map.put(commit.operation().key(), commit.operation().value());
 *       for (ServerSession listener : listeners) {
 *         listener.publish(commit.operation().key(), commit.operation().value());
 *       }
 *     }
 *
 *     public void close(ServerSession session) {
 *       listeners.remove(session);
 *     }
 *   }
 *   }
 * 
* * @author register(ResourceCommand.Register.class, this::register); wrappedExecutor.register(ResourceCommand.Unregister.class, this::unregister); wrappedExecutor.register(ResourceCommand.Delete.class, this::delete); super.init(wrappedExecutor); } @Override public void register(ServerSession session) { } @Override public void unregister(ServerSession session) { } @Override public void expire(ServerSession session) { } @Override public void close(ServerSession session) { } /** * Notifies all subscribed clients of an event. * * @param event The event message. */ protected void notify(Resource.Event event) { Set sessions = eventListeners.get(event.type().id()); if (sessions != null) { for (ServerSession session : sessions) { session.publish("event", new ResourceEvent(event.type().id(), event)); } } } /** * Returns the resource configuration. */ private Properties config(Commit commit) { try { return config; } finally { commit.close(); } } /** * Registers an event listener. */ private void register(Commit commit) { Set sessions = eventListeners.computeIfAbsent(commit.command().event(), k -> new HashSet<>()); sessions.add(commit.session()); } /** * Unregisters an event listener. */ private void unregister(Commit commit) { Set sessions = eventListeners.computeIfAbsent(commit.command().event(), k -> new HashSet<>()); sessions.remove(commit.session()); if (sessions.isEmpty()) { eventListeners.remove(commit.command().event()); } } /** * Handles a delete command. */ private void delete(Commit commit) { try { delete(); } finally { commit.close(); } } /** * Cleans up deleted state machine state. *

* This method is called when a client {@link Resource#delete() deletes} a resource in the cluster. * This method is guaranteed to eventually be called on all servers at the same point in logical time * (i.e. {@code index}). State machines should override this method to clean up state machine state, * including releasing {@link Commit}s held by the state machine. *

   *   {@code
   *   public class MapStateMachine extends ResourceStateMachine {
   *     private final Map> map = new HashMap<>();
   *
   *     public void put(Commit commit) {
   *       map.put(commit.operation().key(), commit);
   *     }
   *
   *     public void delete() {
   *       for (Map.Entry> entry : map.entrySet()) {
   *         entry.getValue().close();
   *       }
   *       map.clear();
   *     }
   *   }
   *   }
   * 
* Failing to override this method to clean up commits held by a resource state machine is considered * a bug and will eventually result in disk filling up. */ public void delete() { } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy