You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@stanbol.apache.org by rw...@apache.org on 2015/06/12 10:48:15 UTC
svn commit: r1685047 -
/stanbol/branches/release-0.12/commons/opennlp/src/main/java/org/apache/stanbol/commons/opennlp/OpenNLP.java
Author: rwesten
Date: Fri Jun 12 08:48:15 2015
New Revision: 1685047
URL: http://svn.apache.org/r1685047
Log:
STANBOL-1424: merged fix from trunk to 0.12
Modified:
stanbol/branches/release-0.12/commons/opennlp/src/main/java/org/apache/stanbol/commons/opennlp/OpenNLP.java
Modified: stanbol/branches/release-0.12/commons/opennlp/src/main/java/org/apache/stanbol/commons/opennlp/OpenNLP.java
URL: http://svn.apache.org/viewvc/stanbol/branches/release-0.12/commons/opennlp/src/main/java/org/apache/stanbol/commons/opennlp/OpenNLP.java?rev=1685047&r1=1685046&r2=1685047&view=diff
==============================================================================
--- stanbol/branches/release-0.12/commons/opennlp/src/main/java/org/apache/stanbol/commons/opennlp/OpenNLP.java (original)
+++ stanbol/branches/release-0.12/commons/opennlp/src/main/java/org/apache/stanbol/commons/opennlp/OpenNLP.java Fri Jun 12 08:48:15 2015
@@ -22,7 +22,11 @@ import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.nio.charset.Charset;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.locks.ReadWriteLock;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
import opennlp.tools.chunker.Chunker;
import opennlp.tools.chunker.ChunkerME;
@@ -79,7 +83,17 @@ public class OpenNLP {
* TODO: change to use a WeakReferenceMap
*/
protected Map<String,Object> models = new HashMap<String,Object>();
-
+ /**
+ * used to sync access to the {@link #models} and {@link #modelCreationLock}
+ */
+ protected ReadWriteLock modelLock = new ReentrantReadWriteLock();
+ /**
+ * used to avoid loading the same model multiple times in parallel.
+ * The value is a int array with an single element. The int at index zero is
+ * used as reference count. When it reaches zero the mapping can be deleted
+ * from the map.
+ */
+ protected Map<String,int[]> modelCreationLock = new HashMap<String,int[]>();
/**
* Default constructor
*/
@@ -464,88 +478,158 @@ public class OpenNLP {
* @throws IOException on any error while loading the model data
* @throws IllegalStateException on any Exception while creating the model
*/
- @SuppressWarnings("unchecked")
private <T> T initModel(String name,Class<T> modelType, Map<String,String> modelProperties) throws InvalidFormatException, IOException {
- Object model = models.get(name);
- if(model != null) {
- if(modelType.isAssignableFrom(model.getClass())){
- return (T) model;
- } else {
- throw new IllegalStateException(String.format(
- "Incompatible Model Types for name '%s': present=%s | requested=%s",
- name,model.getClass(),modelType));
- }
- } else { //create new model
- if(modelProperties != null){ //copy the data to avoid external modifications
- modelProperties = new HashMap<String,String>(modelProperties);
- }else {
- modelProperties = new HashMap<String,String>();
- }
- if(!modelProperties.containsKey("Description")){
- modelProperties.put("Description", "Statistical model for OpenNLP");
- }
- if(!modelProperties.containsKey("Model Type")){
- modelProperties.put("Model Type", modelType.getSimpleName());
- }
- if(!modelProperties.containsKey("Download Location")){
- modelProperties.put("Download Location", DOWNLOAD_ROOT+name);
+ T model = getCachedModel(name, modelType);
+ if(model != null){
+ return model;
+ } //else create the model
+ //We need to avoid creating a model twice in parallel
+ modelLock.writeLock().lock();
+ int[] lock;
+ try {
+ lock = modelCreationLock.get(name);
+ if(lock == null){
+ lock = new int[]{0};
+ modelCreationLock.put(name, lock);
}
- InputStream modelDataStream;
- try {
- modelDataStream = lookupModelStream(name,modelProperties);
- } catch (IOException e) {
- log.debug("Unable to load Resource {} via the DataFileProvider",name);
- return null;
+ lock[0]++;
+ } finally {
+ modelLock.writeLock().unlock();
+ }
+ try {
+ //create only one model with the same name in parallel
+ synchronized (lock) {
+ //now we have the lock ...
+ // first check if it was created while we where waiting for the lock
+ model = getCachedModel(name, modelType);
+ if(model != null){
+ return model;
+ }
+ //not created in the meantime ... we need to create it!
+ T built = loadModel(name, modelType, modelProperties);
+ //register the model
+ modelLock.writeLock().lock();
+ try {
+ models.put(name, built);
+ } finally {
+ modelLock.writeLock().unlock();
+ }
+ return built;
}
- if(modelDataStream == null){
- log.debug("Unable to load Resource {} via the DataFileProvider",name);
- return null;
+ } finally {
+ //we do no longer need the lock
+ lock[0]--;
+ //check if we need to clean up the modelCreationLock map
+ if(lock[0] == 0){
+ modelLock.writeLock().lock();
+ try {
+ if(lock[0] == 0){
+ modelCreationLock.remove(name);
+ }
+ } finally {
+ modelLock.writeLock().unlock();
+ }
}
- T built;
- try {
- Constructor<T> constructor;
- constructor = modelType.getConstructor(InputStream.class);
- built = constructor.newInstance(modelDataStream);
- } catch (SecurityException e) {
- throw new IllegalStateException(String.format(
- "Unable to create %s for %s!",modelType.getSimpleName(),
- name),e);
- } catch (NoSuchMethodException e) {
- throw new IllegalStateException(String.format(
- "Unable to create %s for %s!",modelType.getSimpleName(),
- name),e);
- } catch (IllegalArgumentException e) {
- throw new IllegalStateException(String.format(
- "Unable to create %s for %s!",modelType.getSimpleName(),
- name),e);
- } catch (InstantiationException e) {
- throw new IllegalStateException(String.format(
- "Unable to create %s for %s!",modelType.getSimpleName(),
- name),e);
- } catch (IllegalAccessException e) {
+ }
+ }
+ private <T> T loadModel(String name, Class<T> modelType,
+ Map<String, String> modelProperties) throws InvalidFormatException,
+ IOException {
+ if(modelProperties != null){ //copy the data to avoid external modifications
+ modelProperties = new HashMap<String,String>(modelProperties);
+ }else {
+ modelProperties = new HashMap<String,String>();
+ }
+ if(!modelProperties.containsKey("Description")){
+ modelProperties.put("Description", "Statistical model for OpenNLP");
+ }
+ if(!modelProperties.containsKey("Model Type")){
+ modelProperties.put("Model Type", modelType.getSimpleName());
+ }
+ if(!modelProperties.containsKey("Download Location")){
+ modelProperties.put("Download Location", DOWNLOAD_ROOT+name);
+ }
+ InputStream modelDataStream;
+ try {
+ modelDataStream = lookupModelStream(name,modelProperties);
+ } catch (IOException e) {
+ log.debug("Unable to load Resource {} via the DataFileProvider",name);
+ return null;
+ }
+ if(modelDataStream == null){
+ log.debug("Unable to load Resource {} via the DataFileProvider",name);
+ return null;
+ }
+ T built;
+ try {
+ Constructor<T> constructor;
+ constructor = modelType.getConstructor(InputStream.class);
+ built = constructor.newInstance(modelDataStream);
+ } catch (SecurityException e) {
+ throw new IllegalStateException(String.format(
+ "Unable to create %s for %s!",modelType.getSimpleName(),
+ name),e);
+ } catch (NoSuchMethodException e) {
+ throw new IllegalStateException(String.format(
+ "Unable to create %s for %s!",modelType.getSimpleName(),
+ name),e);
+ } catch (IllegalArgumentException e) {
+ throw new IllegalStateException(String.format(
+ "Unable to create %s for %s!",modelType.getSimpleName(),
+ name),e);
+ } catch (InstantiationException e) {
+ throw new IllegalStateException(String.format(
+ "Unable to create %s for %s!",modelType.getSimpleName(),
+ name),e);
+ } catch (IllegalAccessException e) {
+ throw new IllegalStateException(String.format(
+ "Unable to create %s for %s!",modelType.getSimpleName(),
+ name),e);
+ } catch (InvocationTargetException e) {
+ //this indicates an exception while creating the instance
+ //for InvalidFormatException and IO Exceptions we shall
+ //directly throw the cause. for all others wrap the thrown one
+ //in an IllegalStateException
+ Throwable checked = e.getCause();
+ if (checked instanceof InvalidFormatException){
+ throw (InvalidFormatException)checked;
+ } else if(checked instanceof IOException){
+ throw (IOException)checked;
+ } else {
throw new IllegalStateException(String.format(
"Unable to create %s for %s!",modelType.getSimpleName(),
name),e);
- } catch (InvocationTargetException e) {
- //this indicates an exception while creating the instance
- //for InvalidFormatException and IO Exceptions we shall
- //directly throw the cause. for all others wrap the thrown one
- //in an IllegalStateException
- Throwable checked = e.getCause();
- if (checked instanceof InvalidFormatException){
- throw (InvalidFormatException)checked;
- } else if(checked instanceof IOException){
- throw (IOException)checked;
+ }
+ } finally {
+ IOUtils.closeQuietly(modelDataStream);
+ }
+ return built;
+ }
+ /**
+ * Used to retrieve a model of the parsed model type from the internal cache
+ * @param name the name of the model
+ * @param modelType the type of the model
+ * @return the model or <code>null</code> if not cached
+ * @throws IllegalStateException if the cached model does not have the
+ * expected type
+ */
+ private <T> T getCachedModel(String name, Class<T> modelType) {
+ modelLock.readLock().lock();
+ try {
+ Object model = models.get(name);
+ if(model != null) {
+ if(modelType.isAssignableFrom(model.getClass())){
+ return modelType.cast(model);
} else {
throw new IllegalStateException(String.format(
- "Unable to create %s for %s!",modelType.getSimpleName(),
- name),e);
+ "Incompatible Model Types for name '%s': present=%s | requested=%s",
+ name,model.getClass(),modelType));
}
- } finally {
- IOUtils.closeQuietly(modelDataStream);
+ } else {
+ return null;
}
- models.put(name, built);
- return built;
+ } finally {
+ modelLock.readLock().unlock();
}
}
/**