Skip to content

Commit

Permalink
MODE-1119 Improved handling of errors in configuration
Browse files Browse the repository at this point in the history
Changed the way that errors in the repository configurations are handled. Problems with
the initial content file location are reported with better messages (including that the
file cannot be found or might be empty). Also added a second "start" method on the
JcrEngine class that will automatically start all repositories in the configuration,
and will record all problems during repository initialization in the engine's problems.
This is necessary because the repository configurations are not really validated or used
until the repository is accessed; this new method provides a way to prestart all repositories
and, in the process, validate their configurations.
  • Loading branch information
rhauch committed Mar 17, 2011
1 parent 8bf27db commit 6848d48
Show file tree
Hide file tree
Showing 7 changed files with 265 additions and 4 deletions.
19 changes: 17 additions & 2 deletions modeshape-graph/src/main/java/org/modeshape/graph/Graph.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,10 @@
*/
package org.modeshape.graph;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;
Expand Down Expand Up @@ -2904,7 +2907,12 @@ public ImportInto<Conjunction<Graph>> importXmlFrom( String pathToFile ) {
// Try the file name ...
File file = new File(pathToFile);
if (file.exists() && file.canRead()) {
return importXmlFrom(new File(pathToFile).toURI());
try {
InputStream stream = new BufferedInputStream(new FileInputStream(file));
return importXmlFrom(stream);
} catch (FileNotFoundException e) {
return importXmlFrom(file.toURI());
}
}
// See if there is a resource on the classpath ...
ClassLoader classLoader = getClass().getClassLoader();
Expand All @@ -2914,7 +2922,11 @@ public ImportInto<Conjunction<Graph>> importXmlFrom( String pathToFile ) {
}
// Try a URL ...
try {
return importXmlFrom(new URI(pathToFile));
URI uri = new URI(pathToFile);
if (uri.isAbsolute()) {
// Must be absolute if we're going to turn it into a URL to resolve it ...
return importXmlFrom(new URI(pathToFile));
}
} catch (URISyntaxException e) {
// Must not be a URI ...
}
Expand All @@ -2941,6 +2953,9 @@ public ImportInto<Conjunction<Graph>> importXmlFrom( String resourceName,
RuntimeException error = null;
try {
stream = classLoader.getResourceAsStream(resourceName);
if (stream == null) {
throw new IllegalArgumentException(GraphI18n.errorImportingContent.text(sourceName, resourceName));
}
return importXmlFrom(stream);
} catch (RuntimeException e) {
error = e;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,30 @@ RepositoryDefinition<ReturnType> setOption( JcrRepository.Option option,
*/
RepositoryDefinition<ReturnType> registerNamespace( String prefix,
String uri );

/**
* Specify the location of the file containing the initial content for the repository.
*
* @param path the path to the file containing the initial content; may not be null or empty
* @param firstWorkspace the first workspace in which this content should be loaded
* @param otherWorkspaces additional workspaces in which this content should be loaded
* @return the interface used to set the value for the property; never null
*/
RepositoryDefinition<ReturnType> setInitialContent( String path,
String firstWorkspace,
String... otherWorkspaces );

/**
* Specify the location of the file containing the initial content for the repository.
*
* @param file the file containing the initial content; may not be null, and must exist and be readable
* @param firstWorkspace the first workspace in which this content should be loaded
* @param otherWorkspaces additional workspaces in which this content should be loaded
* @return the interface used to set the value for the property; never null
*/
RepositoryDefinition<ReturnType> setInitialContent( File file,
String firstWorkspace,
String... otherWorkspaces );
}

private final Map<String, RepositoryDefinition<? extends JcrConfiguration>> repositoryDefinitions = new HashMap<String, RepositoryDefinition<? extends JcrConfiguration>>();
Expand Down Expand Up @@ -630,6 +654,53 @@ public RepositoryDefinition<ReturnType> addNodeTypes( String pathToCndFile ) {
return addNodeTypes(new File(pathToCndFile));
}

/**
* {@inheritDoc}
*
* @see org.modeshape.jcr.JcrConfiguration.RepositoryDefinition#setInitialContent(java.lang.String, java.lang.String,
* java.lang.String[])
*/
@Override
public RepositoryDefinition<ReturnType> setInitialContent( String path,
String firstWorkspace,
String... otherWorkspaces ) {
CheckArg.isNotEmpty(path, "path");
path = path.trim();
File file = new File(path);
return setInitialContent(file, firstWorkspace, otherWorkspaces);
}

/**
* {@inheritDoc}
*
* @see org.modeshape.jcr.JcrConfiguration.RepositoryDefinition#setInitialContent(java.io.File, java.lang.String,
* java.lang.String[])
*/
@Override
public RepositoryDefinition<ReturnType> setInitialContent( File file,
String firstWorkspace,
String... otherWorkspaces ) {
CheckArg.isNotNull(file, "file");
CheckArg.isNotEmpty(firstWorkspace, "firstWorkspace");
// Create the single comma-separated list of workspace names ...
String workspaces = firstWorkspace.trim();
if (otherWorkspaces.length != 0) {
StringBuilder sb = new StringBuilder();
sb.append(workspaces); // add the first workspace
for (String other : otherWorkspaces) {
if (other == null) continue;
other = other.trim();
if (other.length() == 0) continue;
sb.append(',').append(other);
}
workspaces = sb.toString();
}
createIfMissing(ModeShapeLexicon.INITIAL_CONTENT).with(ModeShapeLexicon.WORKSPACES, workspaces)
.and(ModeShapeLexicon.CONTENT, file.getAbsolutePath())
.and();
return this;
}

public RepositoryDefinition<ReturnType> addNodeTypes( File file ) {
CheckArg.isNotNull(file, "file");
if (file.exists() && file.canRead()) {
Expand Down
36 changes: 34 additions & 2 deletions modeshape-jcr/src/main/java/org/modeshape/jcr/JcrEngine.java
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,7 @@ protected void checkConfiguration( Subgraph configuration ) {
* @throws IllegalStateException if this method is called when already shut down.
* @throws JcrConfigurationException if there is an error in the configuration or any of the services that prevents proper
* startup
* @see #start(boolean)
* @see #shutdown()
*/
@Override
Expand Down Expand Up @@ -231,6 +232,32 @@ public void run() {
}
}

/**
* Start this engine to make it available for use, and optionally start each of the repositories in the configuration. Any
* errors starting the repositories will be logged as problems.
*
* @param validateRepositoryConfigs true if the configurations of each repository should be validated and each repository
* started/initialized, or false otherwise
* @throws IllegalStateException if this method is called when already shut down.
* @throws JcrConfigurationException if there is an error in the configuration or any of the services that prevents proper
* startup
* @see #start()
* @see #shutdown()
*/
public void start( boolean validateRepositoryConfigs ) {
start();
if (validateRepositoryConfigs) {
for (String repositoryName : getRepositoryNames()) {
try {
getRepository(repositoryName);
} catch (Throwable t) {
// Record this in the problems ...
this.problems.addError(t, JcrI18n.errorStartingRepositoryCheckConfiguration, repositoryName, t.getMessage());
}
}
}
}

/**
* {@inheritDoc}
*
Expand Down Expand Up @@ -373,7 +400,7 @@ protected JcrRepository doCreateJcrRepository( String repositoryName ) throws Re
if (repoNode == null) {
// There is no repository with the supplied name ...
throw new PathNotFoundException(Location.create(repoPath), repositoriesPath,
JcrI18n.repositoryDoesNotExist.text(repoName));
JcrI18n.repositoryDoesNotExist.text(readable(repoName)));
}
Property property = repoNode.getProperty(ModeShapeLexicon.SOURCE_NAME);
if (property == null || property.isEmpty()) {
Expand Down Expand Up @@ -435,7 +462,12 @@ protected JcrRepository doCreateJcrRepository( String repositoryName ) throws Re
if (workspaceName != null && workspaceName.trim().length() != 0) {
// Load the content into the workspace with this name ...
sourceGraph.useWorkspace(workspaceName);
sourceGraph.merge(initialContentGraph);
try {
sourceGraph.merge(initialContentGraph);
} catch (RuntimeException e) {
throw new RepositoryException(JcrI18n.unableToImportInitialContent.text(readable(repoName), contentRef),
e);
}
}
}

Expand Down
2 changes: 2 additions & 0 deletions modeshape-jcr/src/main/java/org/modeshape/jcr/JcrI18n.java
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,10 @@ public final class JcrI18n {
public static I18n failedToReadPropertiesFromManifest;
public static I18n failedToReadPropertyFromManifest;
public static I18n errorLoadingNodeTypeDefintions;
public static I18n errorStartingRepositoryCheckConfiguration;
public static I18n unableToFindNodeTypeDefinitionsOnClasspathOrFileOrUrl;
public static I18n unableToFindResourceOnClasspathOrFileOrUrl;
public static I18n unableToImportInitialContent;
public static I18n fileMustExistAndBeReadable;
public static I18n invalidJcrUrl;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,10 @@ fileDoesNotExist = Unable to find or read the file "{0}"
failedToReadPropertiesFromManifest = Error reading manifest properties: {0}
failedToReadPropertyFromManifest = "{0}" property not found in manifest
errorLoadingNodeTypeDefintions = Error loading CND file "{0}": {1}
errorStartingRepositoryCheckConfiguration = Error starting the "{0}" repository (check the configuration): {1}
unableToFindNodeTypeDefinitionsOnClasspathOrFileOrUrl = Unable to find the node type definition file "{0}" on the classpath, as a relative or absolute file, or resolve as a URL
unableToFindResourceOnClasspathOrFileOrUrl = Unable to find "{0}" on the classpath, as a relative or absolute file, or resolve as a URL
unableToImportInitialContent = Unable to import initial content for repository "{0}" from "{1}". Check that this file exists on the file system or classpath.
fileMustExistAndBeReadable = The file at "{0}" must exist and be readable
invalidJcrUrl = A valid JCR URL must be provided to use this method. "{0}" is not a valid JCR URL. Please consult the ModeShape Reference Guide for information on providing a valid JCR URL.

Expand Down
139 changes: 139 additions & 0 deletions modeshape-jcr/src/test/java/org/modeshape/jcr/JcrEngineTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,14 @@
import static org.hamcrest.core.IsNull.notNullValue;
import static org.junit.Assert.assertThat;
import java.net.URL;
import javax.jcr.RepositoryException;
import javax.jcr.Session;
import javax.jcr.Workspace;
import javax.jcr.nodetype.NodeType;
import org.junit.After;
import org.junit.Test;
import org.modeshape.common.FixFor;
import org.modeshape.common.collection.Problem;
import org.modeshape.graph.connector.inmemory.InMemoryRepositorySource;
import org.modeshape.jcr.JcrRepository.Option;

Expand Down Expand Up @@ -243,6 +246,142 @@ public void shouldAllowCreatingWorkspaces() throws Exception {
jcrSession.logout();
}

@FixFor( "MODE-1119" )
@Test
public void shouldCreateRepositoryConfiguredWithNoInitialContent() throws Exception {
configuration = new JcrConfiguration();
configuration.repositorySource("car-source")
.usingClass(InMemoryRepositorySource.class)
.setDescription("The automobile content")
.setProperty("defaultWorkspaceName", "default");
configuration.repository("cars")
.setSource("car-source")
.registerNamespace("car", "http://www.modeshape.org/examples/cars/1.0")
.addNodeTypes(resourceUrl("cars.cnd"))
.setOption(Option.ANONYMOUS_USER_ROLES, ModeShapeRoles.ADMIN);
engine = configuration.build();
assertThat(engine.getProblems().hasErrors(), is(false));
engine.start();
repository = engine.getRepository("cars");
session = repository.login();

assertNodeType("car:Car", false, false, true, false, null, 0, 11, "nt:unstructured", "mix:created");

// Check that the content is not there...
assertThat(session.getRootNode().hasNode("Cars"), is(false));
}

@FixFor( "MODE-1119" )
@Test
public void shouldCreateRepositoryConfiguredWithCorrectInitialContentPath() throws Exception {
configuration = new JcrConfiguration();
configuration.repositorySource("car-source")
.usingClass(InMemoryRepositorySource.class)
.setDescription("The automobile content")
.setProperty("defaultWorkspaceName", "default");
configuration.repository("cars")
.setSource("car-source")
.registerNamespace("car", "http://www.modeshape.org/examples/cars/1.0")
.addNodeTypes(resourceUrl("cars.cnd"))
.setInitialContent("src/test/resources/initialWorkspaceContent.xml", "default")
.setOption(Option.ANONYMOUS_USER_ROLES, ModeShapeRoles.ADMIN);
engine = configuration.build();
assertThat(engine.getProblems().hasErrors(), is(false));
engine.start();
repository = engine.getRepository("cars");
session = repository.login();

assertNodeType("car:Car", false, false, true, false, null, 0, 11, "nt:unstructured", "mix:created");

// Check that the content is there...
assertThat(session.getRootNode().hasNode("Cars"), is(true));
assertThat(session.getNode("/Cars"), is(notNullValue()));
}

@FixFor( "MODE-1119" )
@Test( expected = RepositoryException.class )
public void shouldCreateRepositoryConfiguredWithValidInitialContentPathButEmptyFile() throws Exception {
configuration = new JcrConfiguration();
configuration.repositorySource("car-source")
.usingClass(InMemoryRepositorySource.class)
.setDescription("The automobile content")
.setProperty("defaultWorkspaceName", "default");
configuration.repository("cars")
.setSource("car-source")
.registerNamespace("car", "http://www.modeshape.org/examples/cars/1.0")
.addNodeTypes(resourceUrl("cars.cnd"))
.setInitialContent("src/test/resources/emptyFile.xml", "default")
.setOption(Option.ANONYMOUS_USER_ROLES, ModeShapeRoles.ADMIN);
engine = configuration.build();
assertThat(engine.getProblems().hasErrors(), is(false));
engine.start();
repository = engine.getRepository("cars");
}

@FixFor( "MODE-1119" )
@Test( expected = RepositoryException.class )
public void shouldCreateRepositoryConfiguredWithIncorrectInitialContentPath() throws Exception {
configuration = new JcrConfiguration();
configuration.repositorySource("car-source")
.usingClass(InMemoryRepositorySource.class)
.setDescription("The automobile content")
.setProperty("defaultWorkspaceName", "default");
configuration.repository("cars")
.setSource("car-source")
.registerNamespace("car", "http://www.modeshape.org/examples/cars/1.0")
.addNodeTypes(resourceUrl("cars.cnd"))
.setInitialContent("src/test/resources/blah/blah/blah", "default")
.setOption(Option.ANONYMOUS_USER_ROLES, ModeShapeRoles.ADMIN);
engine = configuration.build();
assertThat(engine.getProblems().hasErrors(), is(false));
engine.start();
repository = engine.getRepository("cars");
}

@FixFor( "MODE-1119" )
@Test
public void shouldRecordProblemWhenStartingRepositoriesConfiguredWithValidInitialContentPathButEmptyFile() throws Exception {
configuration = new JcrConfiguration();
configuration.repositorySource("car-source")
.usingClass(InMemoryRepositorySource.class)
.setDescription("The automobile content")
.setProperty("defaultWorkspaceName", "default");
configuration.repository("cars")
.setSource("car-source")
.registerNamespace("car", "http://www.modeshape.org/examples/cars/1.0")
.addNodeTypes(resourceUrl("cars.cnd"))
.setInitialContent("src/test/resources/emptyFile.xml", "default")
.setOption(Option.ANONYMOUS_USER_ROLES, ModeShapeRoles.ADMIN);
engine = configuration.build();
assertThat(engine.getProblems().hasErrors(), is(false));
engine.start(true);
assertThat(engine.getProblems().hasErrors(), is(true));
assertThat(engine.getProblems().size(), is(1)); // one error
assertThat(engine.getProblems().iterator().next().getStatus(), is(Problem.Status.ERROR));
}

@FixFor( "MODE-1119" )
@Test
public void shouldRecordProblemWhenStartingRepositoriesConfiguredWithIncorrectInitialContentPath() throws Exception {
configuration = new JcrConfiguration();
configuration.repositorySource("car-source")
.usingClass(InMemoryRepositorySource.class)
.setDescription("The automobile content")
.setProperty("defaultWorkspaceName", "default");
configuration.repository("cars")
.setSource("car-source")
.registerNamespace("car", "http://www.modeshape.org/examples/cars/1.0")
.addNodeTypes(resourceUrl("cars.cnd"))
.setInitialContent("src/test/resources/blah/blah/blah", "default")
.setOption(Option.ANONYMOUS_USER_ROLES, ModeShapeRoles.ADMIN);
engine = configuration.build();
assertThat(engine.getProblems().hasErrors(), is(false));
engine.start(true);
assertThat(engine.getProblems().hasErrors(), is(true));
assertThat(engine.getProblems().size(), is(1)); // one error
assertThat(engine.getProblems().iterator().next().getStatus(), is(Problem.Status.ERROR));
}

protected void assertAccessibleWorkspace( Session session,
String workspaceName ) throws Exception {
assertAccessibleWorkspace(session.getWorkspace(), workspaceName);
Expand Down
Empty file.

0 comments on commit 6848d48

Please sign in to comment.