blob: eca4e2bed180986d47b5d436988accfb8c91820d [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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 org.apache.opennlp.ml.maxent;
import org.apache.opennlp.ml.model.MaxentModel;
/**
* A object which can be used to ensure that a Maxent application can swap the
* model currently in use with a new one in a thread-safe manner without
* stopping the servicing of requests. Use this if your maxent application is
* a heavy-weight one or you have only one particular MaxentModel to use with
* your application. If your application class is lightweight and you will be
* creating multiple instances of it with different underlying models, consider
* using a DomainToModelMap object to ensure thread-safe model swapping.
*
* <p>For example, in your application, create a ModelReplacementManager as
* follows:
*
* <pre>
* private final ModelReplacementManager replacementManager =
* new ModelReplacementManager(
* new ModelSetter() {
* public void setModel(MaxentModel m) {
* model = m;
* }
* }
* );
* </pre>
*
* where "model" would be the actual variable name of the model used by your
* application which you wish to be able to swap (you might have other models
* which need their own ModelReplacementManager).
*
* <p>You'll also need a method to swap the model which calls the manager's
* replaceModel(MaxentModel m) method, e.g.,
*
* <pre>
* public void replaceModel (MaxentModel newmod) {
* replacementManager.replaceModel(newmod);
* }
* </pre>
*
* Then, in the code that uses the model, you need to inform the
* ModelReplacementManager when a thread is beginning to use the model and when
* it no longer needs to be sure that the same model is being used. For
* example, it is quite common to evaluate a particular context, get back a
* double[] which has the normalized probabilities of each of the outcomes given
* that context, and then request the name of a particular outcome. The model
* cannot be swapped during that time since the mapping from outcome labels to
* unique will (probably) be different between the different models. So, do as
* follows:
*
* <pre>
* replacementManager.startUsingModel();
* // some code which evaluates the context, e.g.,
* double[] probs = model.eval(someContext);
* // some code which returns a particular outcome
* if (model.getBestOutcome(probs).equals("T") ...
* replacementManager.finishUsingModel();
* </pre>
*
* The manager will then make sure that all requests which are currently being
* serviced are completed before the new model is swapped in. New requests
* which are made while the models are being swapped are forced to wait for the
* swap to finish. These requests will then be serviced by the new model.
*/
public class ModelReplacementManager {
private ModelSetter setter;
private int users = 0;
private boolean replacementCanProceed = true;
private Thread replacementThread = null;
public ModelReplacementManager(ModelSetter ms) {
setter = ms;
}
/**
* Inform the manager that a thread is using the model. If a replacement is
* underway, the thread is forced to join the replacement thread and thus wait
* until it is finished to begin using the model.
*/
public void startUsingModel() {
if (replacementThread != null) {
try {
replacementThread.join();
} catch (InterruptedException e) {
}
}
replacementCanProceed = false;
users++;
}
/**
* Inform the manager that a thread is done using the model, and thus is not
* dependending on it being unchanged.
*/
public void finishUsingModel() {
users--;
if (users <= 0)
replacementCanProceed = true;
}
/**
* Replace the old model with a new one, forcing the replacement to wait until
* all threads using the old model have finished using it.
*
* @param model
* The new model which is being swapped in.
*/
public synchronized void replaceModel(MaxentModel model) {
replacementThread = Thread.currentThread();
while (!replacementCanProceed)
Thread.yield();
setter.setModel(model);
replacementThread = null;
}
}