Index: src/main/java/org/apache/jackrabbit/core/cluster/ClusterNode.java
===================================================================
--- src/main/java/org/apache/jackrabbit/core/cluster/ClusterNode.java (revision 705259)
+++ src/main/java/org/apache/jackrabbit/core/cluster/ClusterNode.java (working copy)
@@ -16,19 +16,27 @@
*/
package org.apache.jackrabbit.core.cluster;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
+import java.io.File;
+import java.io.IOException;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.jcr.RepositoryException;
+
import org.apache.commons.io.FileUtils;
+import org.apache.jackrabbit.core.NodeId;
+import org.apache.jackrabbit.core.cluster.WorkspaceRecord.CreateWorkspaceAction;
import org.apache.jackrabbit.core.config.ClusterConfig;
import org.apache.jackrabbit.core.config.ConfigurationException;
import org.apache.jackrabbit.core.config.JournalConfig;
-import org.apache.jackrabbit.core.NodeId;
import org.apache.jackrabbit.core.journal.AbstractJournal;
+import org.apache.jackrabbit.core.journal.InstanceRevision;
import org.apache.jackrabbit.core.journal.Journal;
+import org.apache.jackrabbit.core.journal.JournalException;
+import org.apache.jackrabbit.core.journal.Record;
import org.apache.jackrabbit.core.journal.RecordConsumer;
-import org.apache.jackrabbit.core.journal.Record;
-import org.apache.jackrabbit.core.journal.JournalException;
-import org.apache.jackrabbit.core.journal.InstanceRevision;
import org.apache.jackrabbit.core.journal.RecordProducer;
import org.apache.jackrabbit.core.nodetype.InvalidNodeTypeDefException;
import org.apache.jackrabbit.core.nodetype.NodeTypeDef;
@@ -33,19 +41,13 @@
import org.apache.jackrabbit.core.nodetype.InvalidNodeTypeDefException;
import org.apache.jackrabbit.core.nodetype.NodeTypeDef;
import org.apache.jackrabbit.core.state.ChangeLog;
+import org.apache.jackrabbit.core.xml.ClonedInputSource;
import org.apache.jackrabbit.uuid.UUID;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
import EDU.oswego.cs.dl.util.concurrent.Mutex;
-import javax.jcr.RepositoryException;
-
-import java.io.File;
-import java.io.IOException;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Collection;
-
/**
* Default clustered node implementation.
*/
@@ -51,7 +53,7 @@
*/
public class ClusterNode implements Runnable,
NamespaceEventChannel, NodeTypeEventChannel, RecordConsumer,
- ClusterRecordProcessor {
+ ClusterRecordProcessor, WorkspaceEventChannel {
/**
* System property specifying a node id to use.
@@ -139,6 +141,11 @@
private NamespaceEventListener namespaceListener;
/**
+ * Create workspace listener
+ */
+ private WorkspaceListener createWorkspaceListener;
+
+ /**
* Node type listener.
*/
private NodeTypeEventListener nodeTypeListener;
@@ -887,6 +894,58 @@
}
}
+ public void process(WorkspaceRecord record) {
+ if (createWorkspaceListener == null) {
+ String msg = "Create Workspace listener unavailable.";
+ log.error(msg);
+ return;
+ }
+ try {
+ if (record.getActionType() == WorkspaceRecord.CREATE_WORKSPACE_ACTION_TYPE) {
+ CreateWorkspaceAction action = record.getCreateWorkspaceAction();
+ createWorkspaceListener.externalWorkspaceCreated(record.getWorkspace(), action.getInputSource());
+ }
+ } catch (RepositoryException e) {
+ String msg = "Unable to create workspace: "
+ + e.getMessage();
+ log.error(msg);
+ }
+ }
+
+ // -----------------------------------------------< CreateWorkspaceChannel >
+
+ public void setListener(WorkspaceListener listener) {
+ createWorkspaceListener = listener;
+ }
+
+ public void workspaceCreated(String workspaceName,
+ ClonedInputSource inputSource) {
+ if (status != STARTED) {
+ log.info("not started: namespace operation ignored.");
+ return;
+ }
+ ClusterRecord record = null;
+ boolean succeeded = false;
+
+ try {
+ record = new WorkspaceRecord(workspaceName, inputSource, producer.append());
+ record.write();
+ record.update();
+ setRevision(record.getRevision());
+ succeeded = true;
+ } catch (JournalException e) {
+ String msg = "Unable to create log entry: " + e.getMessage();
+ log.error(msg);
+ } catch (Throwable e) {
+ String msg = "Unexpected error while creating log entry.";
+ log.error(msg, e);
+ } finally {
+ if (!succeeded && record != null) {
+ record.cancelUpdate();
+ }
+ }
+ }
+
/**
* Invoked when a cluster operation has ended. If successful,
* attempts to fill the journal record and update it, otherwise cancels
Index: src/main/java/org/apache/jackrabbit/core/cluster/ClusterRecordDeserializer.java
===================================================================
--- src/main/java/org/apache/jackrabbit/core/cluster/ClusterRecordDeserializer.java (revision 704752)
+++ src/main/java/org/apache/jackrabbit/core/cluster/ClusterRecordDeserializer.java (working copy)
@@ -55,6 +55,10 @@
clusterRecord = new NodeTypeRecord(record);
clusterRecord.read();
break;
+ case WorkspaceRecord.IDENTIFIER:
+ clusterRecord = new WorkspaceRecord(record);
+ clusterRecord.read();
+ break;
default:
String msg = "Unknown record identifier: " + c;
throw new JournalException(msg);
Index: src/main/java/org/apache/jackrabbit/core/cluster/ClusterRecordProcessor.java
===================================================================
--- src/main/java/org/apache/jackrabbit/core/cluster/ClusterRecordProcessor.java (revision 704752)
+++ src/main/java/org/apache/jackrabbit/core/cluster/ClusterRecordProcessor.java (working copy)
@@ -52,4 +52,10 @@
* @param record node type record
*/
public void process(NodeTypeRecord record);
+
+ /**
+ * Process a workspace record
+ * @param record workspace record
+ */
+ public void process(WorkspaceRecord record);
}
Index: src/main/java/org/apache/jackrabbit/core/cluster/WorkspaceEventChannel.java
===================================================================
--- src/main/java/org/apache/jackrabbit/core/cluster/WorkspaceEventChannel.java (revision 0)
+++ src/main/java/org/apache/jackrabbit/core/cluster/WorkspaceEventChannel.java (revision 0)
@@ -0,0 +1,30 @@
+/*
+ * 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.jackrabbit.core.cluster;
+
+import org.apache.jackrabbit.core.xml.ClonedInputSource;
+
+/**
+ * Event channel for reporting workspace change events.
+ */
+public interface WorkspaceEventChannel {
+
+ void workspaceCreated(String workspaceName, ClonedInputSource inputSource);
+
+ void setListener(WorkspaceListener listener);
+
+}
Index: src/main/java/org/apache/jackrabbit/core/cluster/WorkspaceListener.java
===================================================================
--- src/main/java/org/apache/jackrabbit/core/cluster/WorkspaceListener.java (revision 0)
+++ src/main/java/org/apache/jackrabbit/core/cluster/WorkspaceListener.java (revision 0)
@@ -0,0 +1,38 @@
+/*
+ * 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.jackrabbit.core.cluster;
+
+import javax.jcr.RepositoryException;
+
+import org.xml.sax.InputSource;
+
+/**
+ * Listener for external workspace changes.
+ */
+public interface WorkspaceListener {
+
+ /**
+ * Workspace created on another cluster node.
+ *
+ * @param workspaceName
+ * @param configTemplate
+ * @throws RepositoryException
+ */
+ public void externalWorkspaceCreated(String workspaceName,
+ InputSource configTemplate) throws RepositoryException;
+
+}
Index: src/main/java/org/apache/jackrabbit/core/cluster/WorkspaceRecord.java
===================================================================
--- src/main/java/org/apache/jackrabbit/core/cluster/WorkspaceRecord.java (revision 0)
+++ src/main/java/org/apache/jackrabbit/core/cluster/WorkspaceRecord.java (revision 0)
@@ -0,0 +1,199 @@
+/*
+ * 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.jackrabbit.core.cluster;
+
+import java.io.ByteArrayInputStream;
+import java.io.CharArrayReader;
+
+import org.apache.jackrabbit.core.journal.JournalException;
+import org.apache.jackrabbit.core.journal.Record;
+import org.apache.jackrabbit.core.xml.ClonedInputSource;
+import org.xml.sax.InputSource;
+
+/**
+ * Record for propagating workspace modifications across the cluster. Currently
+ * only workspace creation is propagated because workspace deletion is not yet
+ * implemented.
+ */
+public class WorkspaceRecord extends ClusterRecord {
+
+ /**
+ * Identifier: NAMESPACE.
+ */
+ static final char IDENTIFIER = 'W';
+
+ /**
+ * Subtype for determining workspace action.
+ */
+ public static final int CREATE_WORKSPACE_ACTION_TYPE = 1;
+
+ /**
+ * Base workspace action
+ */
+ public static abstract class Action {
+ abstract int getType();
+
+ abstract void write(Record record) throws JournalException;
+
+ abstract void read(Record record) throws JournalException;
+ }
+
+ /**
+ * Action for workspace creation.
+ */
+ static final class CreateWorkspaceAction extends Action {
+ private InputSource inputSource;
+ private char charArray[];
+ private byte byteArray[];
+
+ int getType() {
+ return CREATE_WORKSPACE_ACTION_TYPE;
+ }
+
+ CreateWorkspaceAction() {
+
+ }
+
+ CreateWorkspaceAction(ClonedInputSource inputSource) {
+ this.inputSource = inputSource;
+ this.charArray = inputSource.getCharacterArray();
+ this.byteArray = inputSource.getByteArray();
+ }
+
+ void write(Record record) throws JournalException {
+ // store the input source
+ record.writeString(inputSource.getEncoding());
+ record.writeString(inputSource.getPublicId());
+ record.writeString(inputSource.getSystemId());
+
+ // save character array if present
+ if (charArray != null) {
+ record.writeBoolean(true);
+ record.writeString(new String(charArray));
+ } else {
+ record.writeBoolean(false);
+ }
+
+ // save the bytearray if present
+ if (byteArray != null) {
+ record.writeBoolean(true);
+ record.writeInt(byteArray.length);
+ record.write(byteArray);
+ } else {
+ record.writeBoolean(false);
+ }
+ }
+
+ void read(Record record) throws JournalException {
+ // restore the input source
+ inputSource = new InputSource();
+ inputSource.setEncoding(record.readString());
+ inputSource.setPublicId(record.readString());
+ inputSource.setSystemId(record.readString());
+
+ if (record.readBoolean()) {
+ charArray = record.readString().toCharArray();
+ inputSource.setCharacterStream(new CharArrayReader(charArray));
+ }
+ if (record.readBoolean()) {
+ final int size = record.readInt();
+ byteArray = new byte[size];
+ record.readFully(byteArray);
+ inputSource.setByteStream(new ByteArrayInputStream(byteArray));
+ }
+ }
+
+ public InputSource getInputSource() {
+ return inputSource;
+ }
+ }
+
+ // current action
+ private Action action;
+
+ /**
+ * Creates a new {@link WorkspaceRecord} for create workspace action.
+ *
+ * @param workspace
+ * workspace name
+ * @param inputSource
+ * input source with configuration for the workspace
+ * @param record
+ * journal record
+ */
+ protected WorkspaceRecord(String workspace, ClonedInputSource inputSource,
+ Record record) {
+ super(record, workspace);
+
+ action = new CreateWorkspaceAction(inputSource);
+ }
+
+ /**
+ * Creates a new empty {@link WorkspaceRecord}.
+ *
+ * @param record
+ */
+ protected WorkspaceRecord(Record record) {
+ super(record);
+ }
+
+ protected void doRead() throws JournalException {
+
+ workspace = record.readString();
+
+ // determine type
+ int action = record.readInt();
+
+ if (action == CREATE_WORKSPACE_ACTION_TYPE) {
+ this.action = new CreateWorkspaceAction();
+ }
+
+ if (this.action != null) {
+ this.action.read(record);
+ } else {
+ throw new JournalException("Unknown workspace action type");
+ }
+ }
+
+ protected void doWrite() throws JournalException {
+
+ record.writeChar(IDENTIFIER);
+
+ record.writeString(workspace);
+
+ // store the type
+ record.writeInt(getActionType());
+
+ if (action != null) {
+ action.write(record);
+ } else {
+ throw new JournalException("Can not write empty workspace action");
+ }
+ }
+
+ public int getActionType() {
+ return action != null ? action.getType() : -1;
+ }
+
+ public CreateWorkspaceAction getCreateWorkspaceAction() {
+ return (CreateWorkspaceAction) action;
+ }
+
+ public void process(ClusterRecordProcessor processor) {
+ processor.process(this);
+ }
+}
Index: src/main/java/org/apache/jackrabbit/core/config/RepositoryConfig.java
===================================================================
--- src/main/java/org/apache/jackrabbit/core/config/RepositoryConfig.java (revision 704752)
+++ src/main/java/org/apache/jackrabbit/core/config/RepositoryConfig.java (working copy)
@@ -23,6 +23,7 @@
import org.apache.jackrabbit.core.fs.FileSystemException;
import org.apache.jackrabbit.core.fs.FileSystemFactory;
import org.apache.jackrabbit.core.fs.FileSystemPathUtil;
+import org.apache.jackrabbit.core.xml.ClonedInputSource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Element;
@@ -45,6 +46,8 @@
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.Reader;
+import java.io.StringReader;
+import java.io.StringWriter;
import java.io.Writer;
import java.net.URI;
import java.util.Collection;
@@ -340,7 +343,7 @@
+ "for default workspace: " + defaultWorkspace);
}
// create initial default workspace
- createWorkspaceConfig(defaultWorkspace);
+ createWorkspaceConfig(defaultWorkspace, (StringBuffer)null);
}
}
@@ -460,6 +463,8 @@
*
* @param name workspace name
* @param template the workspace template
+ * @param configContent optional stringbuffer that will have the content
+ * of workspace configuration file written in
* @return created workspace configuration
* @throws ConfigurationException if creating the workspace configuration
* failed
@@ -465,7 +470,8 @@
* failed
*/
private synchronized WorkspaceConfig internalCreateWorkspaceConfig(String name,
- Element template)
+ Element template,
+ StringBuffer configContent)
throws ConfigurationException {
// The physical workspace home directory on disk (TODO encode name?)
@@ -511,6 +517,7 @@
try {
// Create the directory
virtualFS.createFolder(configDir);
+
configWriter = new OutputStreamWriter(
virtualFS.getOutputStream(configFile));
} catch (FileSystemException e) {
@@ -537,8 +544,25 @@
TransformerFactory factory = TransformerFactory.newInstance();
Transformer transformer = factory.newTransformer();
transformer.setOutputProperty(OutputKeys.INDENT, "yes");
- transformer.transform(
- new DOMSource(template), new StreamResult(configWriter));
+
+ if (configContent == null)
+ {
+ transformer.transform(
+ new DOMSource(template), new StreamResult(configWriter));
+ }
+ else
+ {
+ StringWriter writer = new StringWriter();
+ transformer.transform(
+ new DOMSource(template), new StreamResult(writer));
+
+ String s = writer.getBuffer().toString();
+ configWriter.write(s);
+ configContent.append(s);
+ }
+ } catch (IOException e) {
+ throw new ConfigurationException(
+ "Cannot create a workspace configuration file", e);
} catch (TransformerConfigurationException e) {
throw new ConfigurationException(
"Cannot create a workspace configuration writer", e);
@@ -585,6 +609,8 @@
* the caller.
*
* @param name workspace name
+ * @param configContent optional StringBuffer that will have the content
+ * of workspace configuration file written in
* @return created workspace configuration
* @throws ConfigurationException if creating the workspace configuration
* failed
@@ -589,10 +615,10 @@
* @throws ConfigurationException if creating the workspace configuration
* failed
*/
- public WorkspaceConfig createWorkspaceConfig(String name)
+ public WorkspaceConfig createWorkspaceConfig(String name, StringBuffer configContent)
throws ConfigurationException {
// use workspace template from repository.xml
- return internalCreateWorkspaceConfig(name, template);
+ return internalCreateWorkspaceConfig(name, template, configContent);
}
/**
@@ -618,7 +644,7 @@
throws ConfigurationException {
ConfigurationParser parser = new ConfigurationParser(new Properties());
Element workspaceTemplate = parser.parseXML(template);
- return internalCreateWorkspaceConfig(name, workspaceTemplate);
+ return internalCreateWorkspaceConfig(name, workspaceTemplate, null);
}
/**
Index: src/main/java/org/apache/jackrabbit/core/RepositoryImpl.java
===================================================================
--- src/main/java/org/apache/jackrabbit/core/RepositoryImpl.java (revision 704752)
+++ src/main/java/org/apache/jackrabbit/core/RepositoryImpl.java (working copy)
@@ -27,6 +27,8 @@
import org.apache.jackrabbit.core.cluster.ClusterContext;
import org.apache.jackrabbit.core.cluster.ClusterException;
import org.apache.jackrabbit.core.cluster.ClusterNode;
+import org.apache.jackrabbit.core.cluster.WorkspaceEventChannel;
+import org.apache.jackrabbit.core.cluster.WorkspaceListener;
import org.apache.jackrabbit.core.cluster.LockEventChannel;
import org.apache.jackrabbit.core.cluster.UpdateEventChannel;
import org.apache.jackrabbit.core.cluster.UpdateEventListener;
@@ -64,6 +66,7 @@
import org.apache.jackrabbit.core.value.InternalValue;
import org.apache.jackrabbit.core.version.VersionManager;
import org.apache.jackrabbit.core.version.VersionManagerImpl;
+import org.apache.jackrabbit.core.xml.ClonedInputSource;
import org.apache.jackrabbit.spi.commons.name.NameConstants;
import org.apache.jackrabbit.spi.commons.namespace.NamespaceResolver;
import org.apache.jackrabbit.spi.commons.namespace.RegistryNamespaceResolver;
@@ -89,6 +92,7 @@
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
+import java.io.StringReader;
import java.security.AccessControlContext;
import java.security.AccessController;
import java.util.Arrays;
@@ -103,7 +107,7 @@
* A RepositoryImpl ...
*/
public class RepositoryImpl extends AbstractRepository
- implements JackrabbitRepository, SessionListener, EventListener {
+ implements JackrabbitRepository, SessionListener, EventListener, WorkspaceListener {
private static Logger log = LoggerFactory.getLogger(RepositoryImpl.class);
@@ -230,6 +234,11 @@
private final ItemStateCacheFactory cacheFactory = new ManagedMLRUItemStateCacheFactory(cacheMgr);
/**
+ * Chanel for posting create workspace messages.
+ */
+ private WorkspaceEventChannel createWorkspaceEventChannel;
+
+ /**
* private constructor
*
* @param repConfig
@@ -292,6 +301,9 @@
clusterNode = createClusterNode();
nsReg.setEventChannel(clusterNode);
ntReg.setEventChannel(clusterNode);
+
+ createWorkspaceEventChannel = clusterNode;
+ clusterNode.setListener(this);
}
// init version manager
@@ -789,16 +801,35 @@
+ workspaceName + "' already exists.");
}
+ // needed to get newly created workspace config file content when runnin in clustered environment
+ StringBuffer workspaceConfigContent = clusterNode != null ? new StringBuffer() : null;
+
// create the workspace configuration
- WorkspaceConfig config = repConfig.createWorkspaceConfig(workspaceName);
+ WorkspaceConfig config = repConfig.createWorkspaceConfig(workspaceName, workspaceConfigContent);
WorkspaceInfo info = createWorkspaceInfo(config);
wspInfos.put(workspaceName, info);
+
+ if (workspaceConfigContent != null && createWorkspaceEventChannel != null) {
+ // notify other cluster node that workspace has been created
+ InputSource s = new InputSource(new StringReader(workspaceConfigContent.toString()));
+ createWorkspaceEventChannel.workspaceCreated(workspaceName, new ClonedInputSource(s));
+ }
}
}
+ public void externalWorkspaceCreated(String workspaceName,
+ InputSource configTemplate) throws RepositoryException {
+
+ createWorkspaceInternal(workspaceName, configTemplate);
+ }
+
/**
* Creates a workspace with the given name and given workspace configuration
* template.
+ *
+ * The difference between this method and {@link #createWorkspace(String, InputSource)}
+ * is that the later notifies the other cluster node that workspace has been created
+ * whereas this method only creates the workspace.
*
* @param workspaceName name of the new workspace
* @param configTemplate the workspace configuration template of the new
@@ -807,7 +838,7 @@
* exists or if another error occurs
* @see SessionImpl#createWorkspace(String,InputSource)
*/
- protected void createWorkspace(String workspaceName,
+ private void createWorkspaceInternal(String workspaceName,
InputSource configTemplate)
throws RepositoryException {
synchronized (wspInfos) {
@@ -822,6 +853,32 @@
wspInfos.put(workspaceName, info);
}
}
+
+ /**
+ * Creates a workspace with the given name and given workspace configuration
+ * template.
+ *
+ * @param workspaceName name of the new workspace
+ * @param configTemplate the workspace configuration template of the new
+ * workspace
+ * @throws RepositoryException if a workspace with the given name already
+ * exists or if another error occurs
+ * @see SessionImpl#createWorkspace(String,InputSource)
+ */
+ protected void createWorkspace(String workspaceName,
+ InputSource configTemplate)
+ throws RepositoryException {
+
+ if (createWorkspaceEventChannel == null) {
+ createWorkspaceInternal(workspaceName, configTemplate);
+ }
+ else {
+
+ ClonedInputSource template = new ClonedInputSource(configTemplate);
+ createWorkspaceInternal(workspaceName, template.cloneInputSource());
+ createWorkspaceEventChannel.workspaceCreated(workspaceName, template);
+ }
+ }
SharedItemStateManager getWorkspaceStateManager(String workspaceName)
throws NoSuchWorkspaceException, RepositoryException {
Index: src/main/java/org/apache/jackrabbit/core/xml/ClonedInputSource.java
===================================================================
--- src/main/java/org/apache/jackrabbit/core/xml/ClonedInputSource.java (revision 0)
+++ src/main/java/org/apache/jackrabbit/core/xml/ClonedInputSource.java (revision 0)
@@ -0,0 +1,168 @@
+/*
+ * 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.jackrabbit.core.xml;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.CharArrayReader;
+import java.io.CharArrayWriter;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.Reader;
+
+import javax.jcr.RepositoryException;
+
+import org.xml.sax.InputSource;
+
+/**
+ * Input source that clones existing input source. After cloning the existing
+ * input source should not be used anymore. To make more copies call
+ * {@link #cloneInputSource()} method.
+ */
+public class ClonedInputSource extends InputSource {
+ private final char characterArray[];
+ private final byte byteArray[];
+
+ /**
+ * Clone existing input source.
+ *
+ * @param input
+ * @throws RepositoryException
+ */
+ public ClonedInputSource(InputSource input) throws RepositoryException {
+
+ if (input == null) {
+ throw new IllegalArgumentException(
+ "Argument 'input' may not be null.");
+ }
+
+ characterArray = read(input.getCharacterStream());
+ byteArray = read(input.getByteStream());
+
+ setEncoding(input.getEncoding());
+ setPublicId(input.getPublicId());
+ setSystemId(input.getSystemId());
+ if (characterArray != null) {
+ setCharacterStream(new CharArrayReader(characterArray));
+ }
+ if (byteArray != null) {
+ setByteStream(new ByteArrayInputStream(byteArray));
+ }
+ }
+
+ private ClonedInputSource(char characterArray[], byte byteArray[]) {
+ super();
+ this.characterArray = characterArray;
+ this.byteArray = byteArray;
+ }
+
+ public char[] getCharacterArray() {
+ return characterArray;
+ }
+
+ public byte[] getByteArray() {
+ return byteArray;
+ }
+
+ /**
+ * Make a clone if this input source. The input source being cloned is still
+ * valid after cloning.
+ *
+ * @return input source clone.
+ */
+ public ClonedInputSource cloneInputSource() {
+
+ ClonedInputSource res = new ClonedInputSource(characterArray, byteArray);
+
+ res.setEncoding(getEncoding());
+ res.setPublicId(getPublicId());
+ res.setSystemId(getSystemId());
+
+ if (byteArray != null) {
+ res.setByteStream(new ByteArrayInputStream(byteArray));
+ }
+ if (characterArray != null) {
+ res.setCharacterStream(new CharArrayReader(characterArray));
+ }
+
+ return res;
+ }
+
+ private static byte[] read(InputStream stream) throws RepositoryException {
+ if (stream != null) {
+ try {
+ final int bufferSize = Math.min(stream.available(), 4096);
+ ByteArrayOutputStream s = new ByteArrayOutputStream(bufferSize);
+
+ byte buffer[] = new byte[bufferSize];
+ while (true) {
+ int numRead = stream.read(buffer);
+ if (numRead > 0) {
+ s.write(buffer, 0, numRead);
+ }
+ if (numRead != bufferSize) {
+ break;
+ }
+ }
+
+ return s.toByteArray();
+ } catch (IOException e) {
+ throw new RepositoryException(e);
+ } finally {
+ try {
+ stream.close();
+ } catch (IOException ignore) {
+
+ }
+ }
+ } else {
+ return null;
+ }
+ }
+
+ private static char[] read(Reader reader) throws RepositoryException {
+ if (reader != null) {
+ try {
+ final int bufferSize = 4096;
+ CharArrayWriter w = new CharArrayWriter(bufferSize);
+
+ char buffer[] = new char[bufferSize];
+ while (true) {
+ int numRead = reader.read(buffer);
+ if (numRead > 0) {
+ w.write(buffer, 0, numRead);
+ }
+ if (numRead != bufferSize) {
+ break;
+ }
+ }
+ return w.toCharArray();
+ } catch (IOException e) {
+ throw new RepositoryException(e);
+ } finally {
+ try {
+ reader.close();
+ } catch (IOException ignore) {
+
+ }
+ }
+ } else {
+ return null;
+ }
+
+ }
+}
\ No newline at end of file
Index: src/test/java/org/apache/jackrabbit/core/config/RepositoryConfigTest.java
===================================================================
--- src/test/java/org/apache/jackrabbit/core/config/RepositoryConfigTest.java (revision 704752)
+++ src/test/java/org/apache/jackrabbit/core/config/RepositoryConfigTest.java (working copy)
@@ -220,7 +220,7 @@
public void testCreateWorkspaceConfig() throws Exception {
RepositoryConfig config =
RepositoryConfig.create(REPOSITORY_XML, REPOSITORY_HOME);
- config.createWorkspaceConfig("test-workspace");
+ config.createWorkspaceConfig("test-workspace", (StringBuffer)null);
File workspaces_dir = new File(REPOSITORY_HOME, "workspaces");
File workspace_dir = new File(workspaces_dir, "test-workspace");
File workspace_xml = new File(workspace_dir, "workspace.xml");
@@ -231,7 +231,7 @@
try {
RepositoryConfig config =
RepositoryConfig.create(REPOSITORY_XML, REPOSITORY_HOME);
- config.createWorkspaceConfig("default");
+ config.createWorkspaceConfig("default", (StringBuffer)null);
fail("No exception thrown when creating a duplicate workspace");
} catch (ConfigurationException e) {
// test passed