<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.argeo.commons</groupId>
<relativePath>..</relativePath>
</parent>
<artifactId>org.argeo.dep.cms</artifactId>
- <name>Commons CMS Dependencies</name>
- <build>
- <plugins>
- <plugin>
- <groupId>org.apache.felix</groupId>
- <artifactId>maven-bundle-plugin</artifactId>
- <configuration>
- <instructions>
- <SLC-ModularDistribution>default</SLC-ModularDistribution>
- </instructions>
- </configuration>
- </plugin>
- <plugin>
- <groupId>org.argeo.maven.plugins</groupId>
- <artifactId>maven-argeo-osgi-plugin</artifactId>
- <executions>
- <execution>
- <id>generate-descriptors</id>
- <goals>
- <goal>descriptors</goal>
- </goals>
- <phase>generate-resources</phase>
- </execution>
- </executions>
- </plugin>
- </plugins>
- </build>
+ <name>Commons CMS Light</name>
<dependencies>
<dependency>
<groupId>org.argeo.commons</groupId>
</exclusion>
<!-- Workbench -->
- <exclusion>
- <groupId>org.argeo.tp.rap.platform</groupId>
- <artifactId>javax.xml</artifactId>
- </exclusion>
<exclusion>
<groupId>org.argeo.tp.rap.platform</groupId>
<artifactId>org.eclipse.rap.ui.workbench</artifactId>
<!-- <groupId>org.argeo.tp</groupId> -->
<!-- <artifactId>org.aspectj.weaver</artifactId> -->
<!-- </exclusion> -->
- <!-- <exclusion> -->
- <!-- <groupId>org.argeo.tp.apache.ant</groupId> -->
- <!-- <artifactId>org.apache.ant</artifactId> -->
- <!-- </exclusion> -->
- <!-- <exclusion> -->
- <!-- <groupId>org.argeo.tp.apache.ant</groupId> -->
- <!-- <artifactId>org.apache.ant.launch</artifactId> -->
- <!-- </exclusion> -->
+ <exclusion>
+ <groupId>org.argeo.tp.apache.ant</groupId>
+ <artifactId>org.apache.ant</artifactId>
+ </exclusion>
+ <exclusion>
+ <groupId>org.argeo.tp.apache.ant</groupId>
+ <artifactId>org.apache.ant.launch</artifactId>
+ </exclusion>
</exclusions>
</dependency>
</dependencies>
- <profiles>
- <profile>
- <id>check-osgi</id>
- <build>
- <plugins>
- <plugin>
- <groupId>org.argeo.maven.plugins</groupId>
- <artifactId>maven-argeo-osgi-plugin</artifactId>
- <executions>
- <execution>
- <id>check-osgi</id>
- <phase>test</phase>
- <goals>
- <goal>equinox</goal>
- </goals>
- <configuration>
- <onlyCheck>true</onlyCheck>
- </configuration>
- </execution>
- </executions>
- </plugin>
- </plugins>
- </build>
- </profile>
- </profiles>
</project>
\ No newline at end of file
<relativePath>..</relativePath>
</parent>
<artifactId>org.argeo.security.dep.node.rap</artifactId>
- <name>Node Eclipse RAP</name>
- <build>
- <plugins>
- <plugin>
- <groupId>org.apache.felix</groupId>
- <artifactId>maven-bundle-plugin</artifactId>
- <configuration>
- <instructions>
- <SLC-ModularDistribution>default</SLC-ModularDistribution>
- </instructions>
- </configuration>
- </plugin>
- <plugin>
- <groupId>org.argeo.maven.plugins</groupId>
- <artifactId>maven-argeo-osgi-plugin</artifactId>
- <executions>
- <execution>
- <id>generate-descriptors</id>
- <goals>
- <goal>descriptors</goal>
- </goals>
- <phase>generate-resources</phase>
- </execution>
- </executions>
- </plugin>
- </plugins>
- </build>
+ <name>Commons CMS Full</name>
<dependencies>
- <!-- CMS kernel -->
+ <!-- CMS -->
<dependency>
<groupId>org.argeo.commons</groupId>
<artifactId>org.argeo.cms</artifactId>
<artifactId>org.argeo.security.ui.admin</artifactId>
<version>2.1.14-SNAPSHOT</version>
</dependency>
- <!-- <dependency> -->
- <!-- <groupId>org.argeo.commons</groupId> -->
- <!-- <artifactId>org.argeo.security.mvc</artifactId> -->
- <!-- <version>2.1.12-SNAPSHOT</version> -->
- <!-- </dependency> -->
<!-- Basis -->
<dependency>
<artifactId>org.argeo.security.dep.node</artifactId>
<version>2.1.14-SNAPSHOT</version>
<type>pom</type>
- <exclusions>
- <!-- <exclusion> <groupId>org.argeo.commons</groupId> <artifactId>org.argeo.security.dao.jackrabbit</artifactId>
- </exclusion> -->
- <!-- <exclusion> <groupId>org.argeo.commons</groupId> <artifactId>org.argeo.security.dao.os</artifactId>
- </exclusion> -->
- </exclusions>
</dependency>
<dependency>
<groupId>org.argeo.commons</groupId>
<artifactId>argeo-tp</artifactId>
<version>${version.argeo-distribution}</version>
</dependency>
-
- <!-- RAP -->
- <!-- <dependency> <groupId>org.argeo.commons</groupId> <artifactId>org.argeo.eclipse.dep.rap</artifactId>
- <version>2.1.12-SNAPSHOT</version> <type>pom</type> </dependency> -->
- <!-- For Tomcat deployment <dependency> <groupId>org.argeo.commons</groupId>
- <artifactId>org.argeo.server.rap.webapp</artifactId> <version>2.1.1.tp-SNAPSHOT</version>
- </dependency> -->
</dependencies>
- <profiles>
- <profile>
- <id>check-osgi</id>
- <build>
- <plugins>
- <plugin>
- <groupId>org.argeo.maven.plugins</groupId>
- <artifactId>maven-argeo-osgi-plugin</artifactId>
- <executions>
- <execution>
- <id>check-osgi</id>
- <phase>test</phase>
- <goals>
- <goal>equinox</goal>
- </goals>
- <configuration>
- <onlyCheck>true</onlyCheck>
- </configuration>
- </execution>
- </executions>
- </plugin>
- </plugins>
- </build>
- </profile>
- </profiles>
</project>
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.argeo.commons</groupId>
<relativePath>..</relativePath>
</parent>
<artifactId>org.argeo.security.dep.node</artifactId>
- <name>Node Backend</name>
- <build>
- <plugins>
- <plugin>
- <groupId>org.apache.felix</groupId>
- <artifactId>maven-bundle-plugin</artifactId>
- <configuration>
- <instructions>
- <SLC-ModularDistribution>default</SLC-ModularDistribution>
- </instructions>
- </configuration>
- </plugin>
- <plugin>
- <groupId>org.argeo.maven.plugins</groupId>
- <artifactId>maven-argeo-osgi-plugin</artifactId>
- <executions>
- <execution>
- <id>generate-descriptors</id>
- <goals>
- <goal>descriptors</goal>
- </goals>
- <phase>generate-resources</phase>
- </execution>
- </executions>
- </plugin>
- </plugins>
- </build>
+ <name>Commons Backend</name>
<dependencies>
<!-- Third Parties -->
<dependency>
<version>${version.argeo-distribution}</version>
</dependency>
- <!-- Default JCR repositories configurations -->
- <!-- <dependency> -->
- <!-- <groupId>org.argeo.commons</groupId> -->
- <!-- <artifactId>org.argeo.node.repo.jackrabbit</artifactId> -->
- <!-- <version>2.1.13-SNAPSHOT</version> -->
- <!-- </dependency> -->
-
<!-- OSGi Boot (and Equinox) -->
<dependency>
<groupId>org.argeo.commons</groupId>
<version>2.1.14-SNAPSHOT</version>
</dependency>
- <!-- Jackrabbit -->
+ <!-- JCR / Jackrabbit -->
<dependency>
<groupId>org.argeo.commons</groupId>
- <artifactId>org.argeo.server.jackrabbit</artifactId>
+ <artifactId>org.argeo.server.jcr</artifactId>
<version>2.1.14-SNAPSHOT</version>
</dependency>
<dependency>
<artifactId>org.argeo.security.jackrabbit</artifactId>
<version>2.1.14-SNAPSHOT</version>
</dependency>
-
- <!-- Security (Jackrabbit) -->
- <!-- <dependency> -->
- <!-- <groupId>org.argeo.commons</groupId> -->
- <!-- <artifactId>org.argeo.security.dao.jackrabbit</artifactId> -->
- <!-- <version>2.1.13-SNAPSHOT</version> -->
- <!-- </dependency> -->
</dependencies>
- <profiles>
- <profile>
- <id>check-osgi</id>
- <build>
- <plugins>
- <plugin>
- <groupId>org.argeo.maven.plugins</groupId>
- <artifactId>maven-argeo-osgi-plugin</artifactId>
- <executions>
- <execution>
- <id>check-osgi</id>
- <phase>test</phase>
- <goals>
- <goal>equinox</goal>
- </goals>
- <configuration>
- <onlyCheck>true</onlyCheck>
- </configuration>
- </execution>
- </executions>
- </plugin>
- </plugins>
- </build>
- </profile>
- </profiles>
</project>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.argeo.commons</groupId>
<relativePath>..</relativePath>
</parent>
<artifactId>dep</artifactId>
- <name>Commons Base Dependencies</name>
+ <name>Commons Modular Distributions</name>
<packaging>pom</packaging>
<modules>
<module>org.argeo.security.dep.node</module>
<module>org.argeo.dep.cms</module>
<module>org.argeo.security.dep.node.rap</module>
</modules>
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-bundle-plugin</artifactId>
+ <configuration>
+ <instructions>
+ <SLC-ModularDistribution>default</SLC-ModularDistribution>
+ </instructions>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.argeo.maven.plugins</groupId>
+ <artifactId>maven-argeo-osgi-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>generate-descriptors</id>
+ <goals>
+ <goal>descriptors</goal>
+ </goals>
+ <phase>generate-resources</phase>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
<profiles>
<profile>
<id>check-osgi</id>
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.argeo.maven.plugins</groupId>
+ <artifactId>maven-argeo-osgi-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>check-osgi</id>
+ <phase>test</phase>
+ <goals>
+ <goal>equinox</goal>
+ </goals>
+ <configuration>
+ <onlyCheck>true</onlyCheck>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
<dependencies>
<dependency>
<groupId>org.argeo.commons</groupId>
</parent>
<artifactId>osgi-boot</artifactId>
<packaging>pom</packaging>
- <name>Commons OSGi Boot Distribution</name>
+ <name>Commons Deployable OSGi Boot</name>
<properties>
- <version.equinox>3.9.1.v20140110-1610</version.equinox>
+ <version.equinox>3.10.1.v20140909-1633</version.equinox>
</properties>
<profiles>
<profile>
<relativePath>..</relativePath>
</parent>
<artifactId>dist</artifactId>
- <name>Commons Distributions</name>
+ <name>Commons Deployable Distributions</name>
<packaging>pom</packaging>
<modules>
<module>osgi-boot</module>
org.xml.sax;version="0.0.0",\
org.eclipse.swt.widgets;version="0.0.0",\
org.argeo.jcr,\
-org.h2;resolution:=optional,\
org.springframework.context,\
org.apache.jackrabbit.api,\
org.apache.jackrabbit.commons,\
org.eclipse.rap.rwt.osgi,\
+org.h2;resolution:=optional,\
+org.apache.commons.vfs2.*;resolution:=optional,\
*
<relativePath>..</relativePath>
</parent>
<artifactId>org.argeo.cms</artifactId>
- <name>Commons Content Management System</name>
+ <name>Commons CMS</name>
<packaging>jar</packaging>
<dependencies>
<dependency>
<artifactId>org.argeo.server.jcr</artifactId>
<version>2.1.14-SNAPSHOT</version>
</dependency>
- <dependency>
- <groupId>org.argeo.commons</groupId>
- <artifactId>org.argeo.server.jackrabbit</artifactId>
- <version>2.1.14-SNAPSHOT</version>
- </dependency>
-
<dependency>
<groupId>org.argeo.commons</groupId>
<artifactId>org.argeo.security.core</artifactId>
<version>2.1.14-SNAPSHOT</version>
</dependency>
-
- <!-- Files -->
- <!-- <dependency>
- <groupId>org.argeo.tp.rap.addons</groupId>
- <artifactId>org.eclipse.rap.addons.fileupload</artifactId>
- </dependency>
- <dependency>
- <groupId>org.argeo.tp.rap.addons</groupId>
- <artifactId>org.eclipse.rap.addons.filedialog</artifactId>
- </dependency> -->
-
- <!-- RAP -->
- <!-- <dependency>
- <groupId>org.argeo.tp.rap.platform</groupId>
- <artifactId>org.eclipse.rap.rwt</artifactId>
- </dependency>
- <dependency>
- <groupId>org.argeo.tp.rap.platform</groupId>
- <artifactId>org.eclipse.rap.jface</artifactId>
- </dependency>
- <dependency>
- <groupId>org.argeo.tp.rap.platform</groupId>
- <artifactId>org.eclipse.core.commands</artifactId>
- </dependency> -->
- <!-- Argeo base Eclipse utilities. -->
<dependency>
<groupId>org.argeo.commons</groupId>
<artifactId>org.argeo.eclipse.ui</artifactId>
<version>2.1.14-SNAPSHOT</version>
</dependency>
-
-
- <!-- OSGi -->
-<!-- <dependency> -->
-<!-- <groupId>org.argeo.tp</groupId> -->
-<!-- <artifactId>org.springframework.osgi.core</artifactId> -->
-<!-- </dependency> -->
-<!-- <dependency> -->
-<!-- <groupId>org.argeo.tp.rap.platform</groupId> -->
-<!-- <artifactId>org.eclipse.osgi</artifactId> -->
-<!-- <scope>provided</scope> -->
-<!-- </dependency> -->
</dependencies>
</project>
\ No newline at end of file
import org.osgi.framework.BundleContext;
/** Configures an Argeo CMS RWT application. */
+@SuppressWarnings("deprecation")
public class CmsApplication implements CmsConstants, ApplicationConfiguration,
BundleContextAware {
final static Log log = LogFactory.getLog(CmsApplication.class);
--- /dev/null
+/*
+ * Copyright (C) 2007-2012 Argeo GmbH
+ *
+ * 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 org.argeo.cms.internal.backup;
+
+import org.apache.commons.vfs2.FileObject;
+import org.apache.commons.vfs2.FileSystemManager;
+import org.apache.commons.vfs2.FileSystemOptions;
+import org.apache.commons.vfs2.provider.sftp.SftpFileSystemConfigBuilder;
+import org.argeo.ArgeoException;
+
+/**
+ * Simplify atomic backups implementation, especially by managing VFS.
+ */
+public abstract class AbstractAtomicBackup implements AtomicBackup {
+ private String name;
+ private String compression = "bz2";
+
+ protected abstract void writeBackup(FileObject targetFo);
+
+ public AbstractAtomicBackup() {
+ }
+
+ public AbstractAtomicBackup(String name) {
+ this.name = name;
+ }
+
+ public void init() {
+ if (name == null)
+ throw new ArgeoException("Atomic backup name must be set");
+ }
+
+ public void destroy() {
+
+ }
+
+ @Override
+ public String backup(FileSystemManager fileSystemManager,
+ String backupsBase, BackupContext backupContext,
+ FileSystemOptions opts) {
+ if (name == null)
+ throw new ArgeoException("Atomic backup name must be set");
+
+ FileObject targetFo = null;
+ try {
+ if (backupsBase.startsWith("sftp:"))
+ SftpFileSystemConfigBuilder.getInstance()
+ .setStrictHostKeyChecking(opts, "no");
+ if (compression == null || compression.equals("none"))
+ targetFo = fileSystemManager.resolveFile(backupsBase + '/'
+ + backupContext.getRelativeFolder() + '/' + name, opts);
+ else if (compression.equals("bz2"))
+ targetFo = fileSystemManager.resolveFile("bz2:" + backupsBase
+ + '/' + backupContext.getRelativeFolder() + '/' + name
+ + ".bz2" + "!" + name, opts);
+ else if (compression.equals("gz"))
+ targetFo = fileSystemManager.resolveFile("gz:" + backupsBase
+ + '/' + backupContext.getRelativeFolder() + '/' + name
+ + ".gz" + "!" + name, opts);
+ else
+ throw new ArgeoException("Unsupported compression "
+ + compression);
+
+ writeBackup(targetFo);
+
+ return targetFo.toString();
+ } catch (Exception e) {
+ throw new ArgeoException("Cannot backup " + name + " to "
+ + targetFo, e);
+ } finally {
+ BackupUtils.closeFOQuietly(targetFo);
+ }
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setCompression(String compression) {
+ this.compression = compression;
+ }
+}
--- /dev/null
+/*
+ * Copyright (C) 2007-2012 Argeo GmbH
+ *
+ * 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 org.argeo.cms.internal.backup;
+
+import org.apache.commons.vfs2.FileSystemManager;
+import org.apache.commons.vfs2.FileSystemOptions;
+
+/** Performs the backup of a single component, typically a database dump */
+public interface AtomicBackup {
+ /** Name identifiying this backup */
+ public String getName();
+
+ /**
+ * Retrieves the data of the component in a format that allows to restore
+ * the component
+ *
+ * @param backupContext
+ * the context of this backup
+ * @return the VFS URI of the generated file or directory
+ */
+ public String backup(FileSystemManager fileSystemManager,
+ String backupsBase, BackupContext backupContext,
+ FileSystemOptions opts);
+}
--- /dev/null
+/*
+ * Copyright (C) 2007-2012 Argeo GmbH
+ *
+ * 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 org.argeo.cms.internal.backup;
+
+import java.text.DateFormat;
+import java.util.Date;
+
+/**
+ * Transient information of a given backup, centralizing common information such
+ * as timestamp and location.
+ */
+public interface BackupContext {
+ /** Backup date */
+ public Date getTimestamp();
+
+ /** Formatted backup date */
+ public String getTimestampAsString();
+
+ /** System name */
+ public String getSystemName();
+
+ /** Local base */
+ public String getRelativeFolder();
+
+ /** Date format */
+ public DateFormat getDateFormat();
+}
--- /dev/null
+/*
+ * Copyright (C) 2007-2012 Argeo GmbH
+ *
+ * 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 org.argeo.cms.internal.backup;
+
+import org.apache.commons.vfs2.FileSystemException;
+import org.apache.commons.vfs2.impl.DefaultFileSystemManager;
+import org.apache.commons.vfs2.provider.bzip2.Bzip2FileProvider;
+import org.apache.commons.vfs2.provider.ftp.FtpFileProvider;
+import org.apache.commons.vfs2.provider.gzip.GzipFileProvider;
+import org.apache.commons.vfs2.provider.local.DefaultLocalFileProvider;
+import org.apache.commons.vfs2.provider.ram.RamFileProvider;
+import org.apache.commons.vfs2.provider.sftp.SftpFileProvider;
+import org.apache.commons.vfs2.provider.url.UrlFileProvider;
+import org.argeo.ArgeoException;
+
+/**
+ * Programatically configured VFS file system manager which can be declared as a
+ * bean and associated with a life cycle (methods
+ * {@link DefaultFileSystemManager#init()} and
+ * {@link DefaultFileSystemManager#closet()}). Supports bz2, file, ram, gzip,
+ * ftp, sftp
+ */
+public class BackupFileSystemManager extends DefaultFileSystemManager {
+
+ public BackupFileSystemManager() {
+ super();
+ try {
+ addProvider("file", new DefaultLocalFileProvider());
+ addProvider("bz2", new Bzip2FileProvider());
+ addProvider("ftp", new FtpFileProvider());
+ addProvider("sftp", new SftpFileProvider());
+ addProvider("gzip", new GzipFileProvider());
+ addProvider("ram", new RamFileProvider());
+ setDefaultProvider(new UrlFileProvider());
+ } catch (FileSystemException e) {
+ throw new ArgeoException("Cannot configure backup file provider", e);
+ }
+ }
+}
--- /dev/null
+/*
+ * Copyright (C) 2007-2012 Argeo GmbH
+ *
+ * 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 org.argeo.cms.internal.backup;
+
+import java.text.DateFormat;
+
+import org.apache.commons.vfs2.FileSystemManager;
+import org.apache.commons.vfs2.FileSystemOptions;
+
+/** Purges previous backups */
+public interface BackupPurge {
+ /**
+ * Purge the backups identified by these arguments. Although these are the
+ * same fields as a {@link BackupContext} we don't pass it as argument since
+ * we want to use this interface to purge remote backups as well (that is,
+ * with a different base), or outside the scope of a running backup.
+ */
+ public void purge(FileSystemManager fileSystemManager, String base,
+ String name, DateFormat dateFormat, FileSystemOptions opts);
+}
--- /dev/null
+/*
+ * Copyright (C) 2007-2012 Argeo GmbH
+ *
+ * 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 org.argeo.cms.internal.backup;
+
+import org.apache.commons.vfs2.FileObject;
+
+/** Backup utilities */
+public class BackupUtils {
+ /** Close a file object quietly even if it is null or throws an exception. */
+ public static void closeFOQuietly(FileObject fo) {
+ if (fo != null) {
+ try {
+ fo.close();
+ } catch (Exception e) {
+ // silent
+ }
+ }
+ }
+
+ /** Prevents instantiation */
+ private BackupUtils() {
+ }
+}
--- /dev/null
+/*
+ * Copyright (C) 2007-2012 Argeo GmbH
+ *
+ * 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 org.argeo.cms.internal.backup;
+
+import org.apache.commons.vfs2.FileObject;
+
+/** Backups a MySQL database using mysqldump. */
+public class MySqlBackup extends OsCallBackup {
+ private String mysqldumpLocation = "/usr/bin/mysqldump";
+
+ private String dbUser;
+ private String dbPassword;
+ private String dbName;
+
+ public MySqlBackup() {
+ }
+
+ public MySqlBackup(String dbUser, String dbPassword, String dbName) {
+ this.dbUser = dbUser;
+ this.dbPassword = dbPassword;
+ this.dbName = dbName;
+ init();
+ }
+
+ @Override
+ public void init() {
+ if (getName() == null)
+ setName(dbName + ".mysql");
+ super.init();
+ }
+
+ @Override
+ public void writeBackup(FileObject targetFo) {
+ if (getCommand() == null)
+ setCommand(mysqldumpLocation
+ + " --lock-tables --add-locks --add-drop-table"
+ + " -u ${dbUser} --password=${dbPassword} --databases ${dbName}");
+ getVariables().put("dbUser", dbUser);
+ getVariables().put("dbPassword", dbPassword);
+ getVariables().put("dbName", dbName);
+
+ super.writeBackup(targetFo);
+ }
+
+ public void setDbUser(String dbUser) {
+ this.dbUser = dbUser;
+ }
+
+ public void setDbPassword(String dbPassword) {
+ this.dbPassword = dbPassword;
+ }
+
+ public void setDbName(String dbName) {
+ this.dbName = dbName;
+ }
+
+ public void setMysqldumpLocation(String mysqldumpLocation) {
+ this.mysqldumpLocation = mysqldumpLocation;
+ }
+
+}
--- /dev/null
+/*
+ * Copyright (C) 2007-2012 Argeo GmbH
+ *
+ * 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 org.argeo.cms.internal.backup;
+
+import org.apache.commons.vfs2.FileObject;
+import org.argeo.ArgeoException;
+
+/** Backups an OpenLDAP server using slapcat */
+public class OpenLdapBackup extends OsCallBackup {
+ private String slapcatLocation = "/usr/sbin/slapcat";
+ private String slapdConfLocation = "/etc/openldap/slapd.conf";
+ private String baseDn;
+
+ public OpenLdapBackup() {
+ super();
+ }
+
+ public OpenLdapBackup(String baseDn) {
+ super();
+ this.baseDn = baseDn;
+ }
+
+ @Override
+ public void writeBackup(FileObject targetFo) {
+ if (baseDn == null)
+ throw new ArgeoException("Base DN must be set");
+
+ if (getCommand() == null)
+ setCommand(slapcatLocation
+ + " -f ${slapdConfLocation} -b '${baseDn}'");
+ getVariables().put("slapdConfLocation", slapdConfLocation);
+ getVariables().put("baseDn", baseDn);
+
+ super.writeBackup(targetFo);
+ }
+
+ public void setSlapcatLocation(String slapcatLocation) {
+ this.slapcatLocation = slapcatLocation;
+ }
+
+ public void setSlapdConfLocation(String slapdConfLocation) {
+ this.slapdConfLocation = slapdConfLocation;
+ }
+
+ public void setBaseDn(String baseDn) {
+ this.baseDn = baseDn;
+ }
+
+}
--- /dev/null
+/*
+ * Copyright (C) 2007-2012 Argeo GmbH
+ *
+ * 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 org.argeo.cms.internal.backup;
+
+import java.io.ByteArrayOutputStream;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.commons.exec.CommandLine;
+import org.apache.commons.exec.DefaultExecutor;
+import org.apache.commons.exec.ExecuteException;
+import org.apache.commons.exec.ExecuteStreamHandler;
+import org.apache.commons.exec.Executor;
+import org.apache.commons.exec.PumpStreamHandler;
+import org.apache.commons.io.IOUtils;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.commons.vfs2.FileContent;
+import org.apache.commons.vfs2.FileObject;
+import org.argeo.ArgeoException;
+
+/**
+ * Runs an OS command and save its standard output as a file. Typically used for
+ * MySQL or OpenLDAP dumps.
+ */
+public class OsCallBackup extends AbstractAtomicBackup {
+ private final static Log log = LogFactory.getLog(OsCallBackup.class);
+
+ private String command;
+ private Map<String, String> variables = new HashMap<String, String>();
+ private Executor executor = new DefaultExecutor();
+
+ private Map<String, String> environment = new HashMap<String, String>();
+
+ /** Name of the sudo user, root if "", not sudo if null */
+ private String sudo = null;
+
+ public OsCallBackup() {
+ }
+
+ public OsCallBackup(String name) {
+ super(name);
+ }
+
+ public OsCallBackup(String name, String command) {
+ super(name);
+ this.command = command;
+ }
+
+ @Override
+ public void writeBackup(FileObject targetFo) {
+ String commandToUse = command;
+
+ // sudo
+ if (sudo != null) {
+ if (sudo.equals(""))
+ commandToUse = "sudo " + commandToUse;
+ else
+ commandToUse = "sudo -u " + sudo + " " + commandToUse;
+ }
+
+ CommandLine commandLine = CommandLine.parse(commandToUse, variables);
+ ByteArrayOutputStream errBos = new ByteArrayOutputStream();
+ if (log.isTraceEnabled())
+ log.trace(commandLine.toString());
+
+ try {
+ // stdout
+ FileContent targetContent = targetFo.getContent();
+ // stderr
+ ExecuteStreamHandler streamHandler = new PumpStreamHandler(
+ targetContent.getOutputStream(), errBos);
+ executor.setStreamHandler(streamHandler);
+ executor.execute(commandLine, environment);
+ } catch (ExecuteException e) {
+ byte[] err = errBos.toByteArray();
+ String errStr = new String(err);
+ throw new ArgeoException("Process " + commandLine + " failed ("
+ + e.getExitValue() + "): " + errStr, e);
+ } catch (Exception e) {
+ byte[] err = errBos.toByteArray();
+ String errStr = new String(err);
+ throw new ArgeoException("Process " + commandLine + " failed: "
+ + errStr, e);
+ } finally {
+ IOUtils.closeQuietly(errBos);
+ }
+ }
+
+ public void setCommand(String command) {
+ this.command = command;
+ }
+
+ protected String getCommand() {
+ return command;
+ }
+
+ /**
+ * A reference to the environment variables that will be passed to the
+ * process. Empty by default.
+ */
+ protected Map<String, String> getEnvironment() {
+ return environment;
+ }
+
+ protected Map<String, String> getVariables() {
+ return variables;
+ }
+
+ public void setVariables(Map<String, String> variables) {
+ this.variables = variables;
+ }
+
+ public void setExecutor(Executor executor) {
+ this.executor = executor;
+ }
+
+ public void setSudo(String sudo) {
+ this.sudo = sudo;
+ }
+
+}
--- /dev/null
+/*
+ * Copyright (C) 2007-2012 Argeo GmbH
+ *
+ * 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 org.argeo.cms.internal.backup;
+
+import org.apache.commons.vfs2.FileObject;
+
+/** Backups a PostgreSQL database using pg_dump. */
+public class PostgreSqlBackup extends OsCallBackup {
+ /**
+ * PostgreSQL password environment variable (see
+ * http://stackoverflow.com/questions
+ * /2893954/how-to-pass-in-password-to-pg-dump)
+ */
+ protected final static String PGPASSWORD = "PGPASSWORD";
+
+ private String pgDumpLocation = "/usr/bin/pg_dump";
+
+ private String dbUser;
+ private String dbPassword;
+ private String dbName;
+
+ public PostgreSqlBackup() {
+ super();
+ }
+
+ public PostgreSqlBackup(String dbUser, String dbPassword, String dbName) {
+ this.dbUser = dbUser;
+ this.dbPassword = dbPassword;
+ this.dbName = dbName;
+ init();
+ }
+
+ @Override
+ public void init() {
+ // disable compression since pg_dump is used with -Fc option
+ setCompression(null);
+
+ if (getName() == null)
+ setName(dbName + ".pgdump");
+ super.init();
+ }
+
+ @Override
+ public void writeBackup(FileObject targetFo) {
+ if (getCommand() == null) {
+ getEnvironment().put(PGPASSWORD, dbPassword);
+ setCommand(pgDumpLocation + " -Fc" + " -U ${dbUser} ${dbName}");
+ }
+ getVariables().put("dbUser", dbUser);
+ getVariables().put("dbPassword", dbPassword);
+ getVariables().put("dbName", dbName);
+
+ super.writeBackup(targetFo);
+ }
+
+ public void setDbUser(String dbUser) {
+ this.dbUser = dbUser;
+ }
+
+ public void setDbPassword(String dbPassword) {
+ this.dbPassword = dbPassword;
+ }
+
+ public void setDbName(String dbName) {
+ this.dbName = dbName;
+ }
+
+ public void setPgDumpLocation(String mysqldumpLocation) {
+ this.pgDumpLocation = mysqldumpLocation;
+ }
+
+}
--- /dev/null
+/*
+ * Copyright (C) 2007-2012 Argeo GmbH
+ *
+ * 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 org.argeo.cms.internal.backup;
+
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+import org.apache.commons.vfs2.FileSystemManager;
+
+/** Simple implementation of a backup context */
+public class SimpleBackupContext implements BackupContext {
+ private DateFormat dateFormat = new SimpleDateFormat("yyyyMMdd_HHmm");
+ private final Date timestamp;
+ private final String name;
+
+ private final FileSystemManager fileSystemManager;
+
+ public SimpleBackupContext(FileSystemManager fileSystemManager,
+ String backupsBase, String name) {
+ this.name = name;
+ this.timestamp = new Date();
+ this.fileSystemManager = fileSystemManager;
+ }
+
+ public Date getTimestamp() {
+ return timestamp;
+ }
+
+ public String getTimestampAsString() {
+ return dateFormat.format(timestamp);
+ }
+
+ public String getSystemName() {
+ return name;
+ }
+
+ public String getRelativeFolder() {
+ return name + '/' + getTimestampAsString();
+ }
+
+ public DateFormat getDateFormat() {
+ return dateFormat;
+ }
+
+ public FileSystemManager getFileSystemManager() {
+ return fileSystemManager;
+ }
+
+}
--- /dev/null
+/*
+ * Copyright (C) 2007-2012 Argeo GmbH
+ *
+ * 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 org.argeo.cms.internal.backup;
+
+import java.text.DateFormat;
+import java.util.Date;
+import java.util.SortedMap;
+import java.util.TreeMap;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.commons.vfs2.FileObject;
+import org.apache.commons.vfs2.FileSystemManager;
+import org.apache.commons.vfs2.FileSystemOptions;
+import org.apache.commons.vfs2.Selectors;
+import org.argeo.ArgeoException;
+import org.joda.time.DateTime;
+import org.joda.time.Period;
+
+/** Simple backup purge which keeps backups only for a given number of days */
+public class SimpleBackupPurge implements BackupPurge {
+ private final static Log log = LogFactory.getLog(SimpleBackupPurge.class);
+
+ private Integer daysKept = 30;
+
+ @Override
+ public void purge(FileSystemManager fileSystemManager, String base,
+ String name, DateFormat dateFormat, FileSystemOptions opts) {
+ try {
+ DateTime nowDt = new DateTime();
+ FileObject baseFo = fileSystemManager.resolveFile(
+ base + '/' + name, opts);
+
+ SortedMap<DateTime, FileObject> toDelete = new TreeMap<DateTime, FileObject>();
+ int backupCount = 0;
+
+ // make sure base dir exists
+ baseFo.createFolder();
+
+ // scan backups and list those which should be deleted
+ for (FileObject backupFo : baseFo.getChildren()) {
+ String backupName = backupFo.getName().getBaseName();
+ Date backupDate = dateFormat.parse(backupName);
+ backupCount++;
+
+ DateTime backupDt = new DateTime(backupDate.getTime());
+ Period sinceThen = new Period(backupDt, nowDt);
+ int days = sinceThen.getDays();
+ // int days = sinceThen.getMinutes();
+ if (days > daysKept) {
+ toDelete.put(backupDt, backupFo);
+ }
+ }
+
+ if (toDelete.size() != 0 && toDelete.size() == backupCount) {
+ // all backups would be deleted
+ // but we want to keep at least one
+ DateTime lastBackupDt = toDelete.firstKey();
+ FileObject keptFo = toDelete.remove(lastBackupDt);
+ log.warn("Backup " + keptFo
+ + " kept although it is older than " + daysKept
+ + " days.");
+ }
+
+ // delete old backups
+ for (FileObject backupFo : toDelete.values()) {
+ backupFo.delete(Selectors.SELECT_ALL);
+ if (log.isDebugEnabled())
+ log.debug("Deleted backup " + backupFo);
+ }
+ } catch (Exception e) {
+ throw new ArgeoException("Could not purge previous backups", e);
+ }
+
+ }
+
+}
--- /dev/null
+/*
+ * Copyright (C) 2007-2012 Argeo GmbH
+ *
+ * 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 org.argeo.cms.internal.backup;
+
+import java.io.File;
+
+import org.apache.commons.vfs2.FileObject;
+
+/** Backups a Subversion repository using svnadmin. */
+public class SvnBackup extends OsCallBackup {
+ private String svnadminLocation = "/usr/bin/svnadmin";
+
+ private String repoLocation;
+ private String repoName;
+
+ public SvnBackup() {
+ }
+
+ public SvnBackup(String repoLocation) {
+ this.repoLocation = repoLocation;
+ init();
+ }
+
+ @Override
+ public void init() {
+ // use directory as repo name
+ if (repoName == null)
+ repoName = new File(repoLocation).getName();
+
+ if (getName() == null)
+ setName(repoName + ".svndump");
+ super.init();
+ }
+
+ @Override
+ public void writeBackup(FileObject targetFo) {
+ if (getCommand() == null) {
+ setCommand(svnadminLocation + " dump " + " ${repoLocation}");
+ }
+ getVariables().put("repoLocation", repoLocation);
+
+ super.writeBackup(targetFo);
+ }
+
+ public void setRepoLocation(String repoLocation) {
+ this.repoLocation = repoLocation;
+ }
+
+ public void setRepoName(String repoName) {
+ this.repoName = repoName;
+ }
+
+ public void setSvnadminLocation(String mysqldumpLocation) {
+ this.svnadminLocation = mysqldumpLocation;
+ }
+
+}
--- /dev/null
+/*
+ * Copyright (C) 2007-2012 Argeo GmbH
+ *
+ * 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 org.argeo.cms.internal.backup;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.commons.vfs2.FileObject;
+import org.apache.commons.vfs2.FileSystemException;
+import org.apache.commons.vfs2.FileSystemManager;
+import org.apache.commons.vfs2.FileSystemOptions;
+import org.apache.commons.vfs2.Selectors;
+import org.apache.commons.vfs2.UserAuthenticator;
+import org.apache.commons.vfs2.impl.DefaultFileSystemConfigBuilder;
+import org.argeo.ArgeoException;
+
+/**
+ * Combines multiple backups and transfer them to a remote location. Purges
+ * remote and local data based on certain criteria.
+ */
+public class SystemBackup implements Runnable {
+ private final static Log log = LogFactory.getLog(SystemBackup.class);
+
+ private FileSystemManager fileSystemManager;
+ private UserAuthenticator userAuthenticator = null;
+
+ private String backupsBase;
+ private String systemName;
+
+ private List<AtomicBackup> atomicBackups = new ArrayList<AtomicBackup>();
+ private BackupPurge backupPurge = new SimpleBackupPurge();
+
+ private Map<String, UserAuthenticator> remoteBases = new HashMap<String, UserAuthenticator>();
+
+ @Override
+ public void run() {
+ if (atomicBackups.size() == 0)
+ throw new ArgeoException("No atomic backup listed");
+ List<String> failures = new ArrayList<String>();
+
+ SimpleBackupContext backupContext = new SimpleBackupContext(
+ fileSystemManager, backupsBase, systemName);
+
+ // purge older backups
+ FileSystemOptions opts = new FileSystemOptions();
+ try {
+ DefaultFileSystemConfigBuilder.getInstance().setUserAuthenticator(
+ opts, userAuthenticator);
+ } catch (FileSystemException e) {
+ throw new ArgeoException("Cannot create authentication", e);
+ }
+
+ try {
+
+ backupPurge.purge(fileSystemManager, backupsBase, systemName,
+ backupContext.getDateFormat(), opts);
+ } catch (Exception e) {
+ failures.add("Purge " + backupsBase + " failed: " + e.getMessage());
+ log.error("Purge of " + backupsBase + " failed", e);
+ }
+
+ // perform backup
+ for (AtomicBackup atomickBackup : atomicBackups) {
+ try {
+ String target = atomickBackup.backup(fileSystemManager,
+ backupsBase, backupContext, opts);
+ if (log.isDebugEnabled())
+ log.debug("Performed backup " + target);
+ } catch (Exception e) {
+ String msg = "Atomic backup " + atomickBackup.getName()
+ + " failed: " + ArgeoException.chainCausesMessages(e);
+ failures.add(msg);
+ log.error(msg);
+ if (log.isTraceEnabled())
+ log.trace(
+ "Stacktrace of atomic backup "
+ + atomickBackup.getName() + " failure.", e);
+ }
+ }
+
+ // dispatch to remote
+ for (String remoteBase : remoteBases.keySet()) {
+ FileObject localBaseFo = null;
+ FileObject remoteBaseFo = null;
+ UserAuthenticator auth = remoteBases.get(remoteBase);
+
+ // authentication
+ FileSystemOptions remoteOpts = new FileSystemOptions();
+ try {
+ DefaultFileSystemConfigBuilder.getInstance()
+ .setUserAuthenticator(remoteOpts, auth);
+ backupPurge.purge(fileSystemManager, remoteBase, systemName,
+ backupContext.getDateFormat(), remoteOpts);
+ } catch (Exception e) {
+ failures.add("Purge " + remoteBase + " failed: "
+ + e.getMessage());
+ log.error("Cannot purge " + remoteBase, e);
+ }
+
+ try {
+ localBaseFo = fileSystemManager.resolveFile(backupsBase + '/'
+ + backupContext.getRelativeFolder(), opts);
+ remoteBaseFo = fileSystemManager.resolveFile(remoteBase + '/'
+ + backupContext.getRelativeFolder(), remoteOpts);
+ remoteBaseFo.copyFrom(localBaseFo, Selectors.SELECT_ALL);
+ if (log.isDebugEnabled())
+ log.debug("Copied backup to " + remoteBaseFo + " from "
+ + localBaseFo);
+ // }
+ } catch (Exception e) {
+ failures.add("Dispatch to " + remoteBase + " failed: "
+ + e.getMessage());
+ log.error(
+ "Cannot dispatch backups from "
+ + backupContext.getRelativeFolder() + " to "
+ + remoteBase, e);
+ }
+ BackupUtils.closeFOQuietly(localBaseFo);
+ BackupUtils.closeFOQuietly(remoteBaseFo);
+ }
+
+ int failureCount = 0;
+ if (failures.size() > 0) {
+ StringBuffer buf = new StringBuffer();
+ for (String failure : failures) {
+ buf.append('\n').append(failureCount).append(" - ")
+ .append(failure);
+ failureCount++;
+ }
+ throw new ArgeoException(failureCount
+ + " error(s) when running the backup,"
+ + " check the logs and the backups as soon as possible."
+ + buf);
+ }
+ }
+
+ public void setFileSystemManager(FileSystemManager fileSystemManager) {
+ this.fileSystemManager = fileSystemManager;
+ }
+
+ public void setBackupsBase(String backupsBase) {
+ this.backupsBase = backupsBase;
+ }
+
+ public void setSystemName(String name) {
+ this.systemName = name;
+ }
+
+ public void setAtomicBackups(List<AtomicBackup> atomicBackups) {
+ this.atomicBackups = atomicBackups;
+ }
+
+ public void setBackupPurge(BackupPurge backupPurge) {
+ this.backupPurge = backupPurge;
+ }
+
+ public void setUserAuthenticator(UserAuthenticator userAuthenticator) {
+ this.userAuthenticator = userAuthenticator;
+ }
+
+ public void setRemoteBases(Map<String, UserAuthenticator> remoteBases) {
+ this.remoteBases = remoteBases;
+ }
+
+// public static void main(String args[]) {
+// while (true) {
+// try {
+// StandardFileSystemManager fsm = new StandardFileSystemManager();
+// fsm.init();
+//
+// SystemBackup systemBackup = new SystemBackup();
+// systemBackup.setSystemName("mySystem");
+// systemBackup
+// .setBackupsBase("/home/mbaudier/dev/src/commons/server/runtime/org.argeo.server.core/target");
+// systemBackup.setFileSystemManager(fsm);
+//
+// List<AtomicBackup> atomicBackups = new ArrayList<AtomicBackup>();
+//
+// MySqlBackup mySqlBackup = new MySqlBackup("root", "", "test");
+// atomicBackups.add(mySqlBackup);
+// PostgreSqlBackup postgreSqlBackup = new PostgreSqlBackup(
+// "argeo", "argeo", "gis_template");
+// atomicBackups.add(postgreSqlBackup);
+// SvnBackup svnBackup = new SvnBackup(
+// "/home/mbaudier/tmp/testsvnrepo");
+// atomicBackups.add(svnBackup);
+//
+// systemBackup.setAtomicBackups(atomicBackups);
+//
+// Map<String, UserAuthenticator> remoteBases = new HashMap<String, UserAuthenticator>();
+// StaticUserAuthenticator userAuthenticator = new StaticUserAuthenticator(
+// null, "demo", "demo");
+// remoteBases.put("sftp://localhost/home/mbaudier/test",
+// userAuthenticator);
+// systemBackup.setRemoteBases(remoteBases);
+//
+// systemBackup.run();
+//
+// fsm.close();
+// } catch (FileSystemException e) {
+// // TODO Auto-generated catch block
+// e.printStackTrace();
+// System.exit(1);
+// }
+//
+// // wait
+// try {
+// Thread.sleep(120 * 1000);
+// } catch (InterruptedException e) {
+// e.printStackTrace();
+// }
+// }
+// }
+}
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.argeo.cms.CmsException;
-import org.argeo.cms.internal.kernel.NodeHttp.AnonymousFilter;
-import org.argeo.cms.internal.kernel.NodeHttp.DavFilter;
import org.argeo.jackrabbit.servlet.OpenInViewSessionProvider;
import org.argeo.jackrabbit.servlet.RemotingServlet;
import org.argeo.jackrabbit.servlet.WebdavServlet;
private String httpAuthRealm = "Argeo";
// Filters
- private final RootFilter rootFilter;
+ // private final RootFilter rootFilter;
// remoting
private OpenInViewSessionProvider sessionProvider;
+ ExtendedHttpService.class + " service.");
// Filters
- rootFilter = new RootFilter();
+ // rootFilter = new RootFilter();
// DAV
sessionProvider = new OpenInViewSessionProvider();
private final InternalAuthenticationProvider internalAuth;
private final AnonymousAuthenticationProvider anonymousAuth;
- private final JackrabbitUserAdminService jackrabbitUserAdmin;
+ private final JackrabbitUserAdminService userAdminService;
+ // private final JcrUserAdmin userAdmin;
private ServiceRegistration<AuthenticationManager> authenticationManagerReg;
- private ServiceRegistration<UserAdminService> userAdminReg;
+ private ServiceRegistration<UserAdminService> userAdminServiceReg;
private ServiceRegistration<UserDetailsManager> userDetailsManagerReg;
+ // private ServiceRegistration<UserAdmin> userAdminReg;
+
public NodeSecurity(BundleContext bundleContext, JackrabbitNode node)
throws RepositoryException {
URL url = getClass().getClassLoader().getResource(
KernelConstants.DEFAULT_SECURITY_KEY);
// user admin
- jackrabbitUserAdmin = new JackrabbitUserAdminService();
- jackrabbitUserAdmin.setRepository(node);
- jackrabbitUserAdmin.setSecurityModel(new SimpleJcrSecurityModel());
- jackrabbitUserAdmin.init();
+ userAdminService = new JackrabbitUserAdminService();
+ userAdminService.setRepository(node);
+ userAdminService.setSecurityModel(new SimpleJcrSecurityModel());
+ userAdminService.init();
+
+ // userAdmin = new JcrUserAdmin(bundleContext);
+ // userAdmin.setUserAdminService(userAdminService);
}
public void publish() {
authenticationManagerReg = bundleContext.registerService(
AuthenticationManager.class, this, null);
- userAdminReg = bundleContext.registerService(UserAdminService.class,
- jackrabbitUserAdmin, null);
+ userAdminServiceReg = bundleContext.registerService(
+ UserAdminService.class, userAdminService, null);
userDetailsManagerReg = bundleContext.registerService(
- UserDetailsManager.class, jackrabbitUserAdmin, null);
- // userAdminReg =
- // bundleContext.registerService(UserDetailsService.class,
- // jackrabbitUserAdmin, null);
+ UserDetailsManager.class, userAdminService, null);
+ // userAdminReg = bundleContext.registerService(UserAdmin.class,
+ // userAdmin, null);
}
void destroy() {
try {
- jackrabbitUserAdmin.destroy();
+ userAdminService.destroy();
} catch (RepositoryException e) {
log.error("Error while destroying Jackrabbit useradmin");
}
userDetailsManagerReg.unregister();
- userAdminReg.unregister();
+ userAdminServiceReg.unregister();
authenticationManagerReg.unregister();
+ // userAdminReg.unregister();
}
@Override
else if (authentication instanceof AnonymousAuthenticationToken)
auth = anonymousAuth.authenticate(authentication);
else if (authentication instanceof UsernamePasswordAuthenticationToken)
- auth = jackrabbitUserAdmin.authenticate(authentication);
+ auth = userAdminService.authenticate(authentication);
if (auth == null)
throw new CmsException("Could not authenticate " + authentication);
return auth;
import org.apache.commons.logging.LogFactory;
import org.argeo.cms.CmsEditable;
import org.argeo.cms.CmsException;
-import org.argeo.cms.widgets.JcrComposite;
import org.argeo.cms.widgets.ScrolledPage;
import org.eclipse.jface.viewers.ContentViewer;
import org.eclipse.jface.viewers.ISelection;
</parent>
<artifactId>org.argeo.eclipse.ui.rap</artifactId>
<name>Commons Eclipse UI RAP</name>
- <description>Provide rap specific classes and behaviour in order to provide single sourcing abilities</description>
- <packaging>jar</packaging>
<dependencies>
- <!-- Generic base UI classes. -->
<dependency>
<groupId>org.argeo.commons</groupId>
<artifactId>org.argeo.eclipse.ui</artifactId>
<version>2.1.14-SNAPSHOT</version>
</dependency>
-
- <!-- Base distribution for RAP projects -->
- <!-- <dependency>
- <groupId>org.argeo.commons</groupId>
- <artifactId>org.argeo.eclipse.dep.rap</artifactId>
- <type>pom</type>
- <version>2.1.12-SNAPSHOT</version>
- </dependency> -->
-
- <!-- Utils and logging -->
<dependency>
<groupId>org.argeo.commons</groupId>
<artifactId>org.argeo.util</artifactId>
<version>2.1.14-SNAPSHOT</version>
</dependency>
-<!-- <dependency> -->
-<!-- <groupId>org.argeo.tp</groupId> -->
-<!-- <artifactId>slf4j.org.apache.commons.logging</artifactId> -->
-<!-- </dependency> -->
</dependencies>
</project>
\ No newline at end of file
<relativePath>..</relativePath>
</parent>
<artifactId>org.argeo.eclipse.ui.workbench.rap</artifactId>
- <name>Commons Eclipse UI Workbench RAP</name>
+ <name>Commons Eclipse Workbench RAP</name>
</project>
<relativePath>..</relativePath>
</parent>
<artifactId>org.argeo.eclipse.ui.workbench</artifactId>
- <name>Commons Eclipse UI Workbench</name>
+ <name>Commons Eclipse Workbench</name>
<dependencies>
- <!-- Generic Eclipse UI Dependencies -->
<dependency>
<groupId>org.argeo.commons</groupId>
- <artifactId>org.argeo.eclipse.ui</artifactId>
+ <artifactId>org.argeo.util</artifactId>
<version>2.1.14-SNAPSHOT</version>
- <exclusions>
- <!-- version of the org.osgi.framework package provided in this bundle
- is too old -->
- <!-- <exclusion>
- <groupId>org.argeo.tp</groupId>
- <artifactId>org.osgi.core</artifactId>
- </exclusion> -->
- </exclusions>
</dependency>
-
- <!-- We build against RAP -->
<dependency>
<groupId>org.argeo.commons</groupId>
- <artifactId>org.argeo.eclipse.ui.rap</artifactId>
+ <artifactId>org.argeo.eclipse.ui</artifactId>
<version>2.1.14-SNAPSHOT</version>
- <scope>provided</scope>
</dependency>
-
- <!-- <dependency>
- <groupId>org.argeo.commons</groupId>
- <artifactId>org.argeo.eclipse.dep.rap</artifactId>
- <type>pom</type>
- <version>2.1.12-SNAPSHOT</version>
- <scope>provided</scope>
- </dependency> -->
-
- <!-- Business dependencies -->
<dependency>
<groupId>org.argeo.commons</groupId>
- <artifactId>org.argeo.util</artifactId>
+ <artifactId>org.argeo.eclipse.ui.rap</artifactId>
<version>2.1.14-SNAPSHOT</version>
+ <scope>provided</scope>
</dependency>
</dependencies>
-</project>
+</project>
\ No newline at end of file
-Require-Bundle: org.eclipse.core.runtime
Import-Package: org.eclipse.core.commands,\
org.eclipse.jface.window,\
org.eclipse.swt,\
javax.jcr.nodetype,\
org.springframework.security.core,\
*
-
-# Was:
-#Bundle-ActivationPolicy: lazy
-#Bundle-SymbolicName: org.argeo.eclipse.ui;singleton:=true
-#Bundle-Activator: org.argeo.eclipse.ui.ArgeoUiPlugin
-
-#!org.eclipse.core.commands,\
-# !org.eclipse.core.runtime,\
-# !org.eclipse.ui.plugin,\
</parent>
<artifactId>org.argeo.eclipse.ui</artifactId>
<name>Commons Eclipse UI</name>
- <packaging>jar</packaging>
- <!-- <build> <plugins> <plugin> <groupId>org.apache.felix</groupId> <artifactId>maven-bundle-plugin</artifactId>
- <configuration> <instructions> <Bundle-SymbolicName>${project.artifactId};singleton:=true</Bundle-SymbolicName>
- </instructions> </configuration> </plugin> </plugins> </build> -->
<dependencies>
- <!-- We build against RAP -->
- <!-- <dependency> <groupId>org.argeo.commons</groupId> <artifactId>org.argeo.eclipse.dep.rap</artifactId>
- <type>pom</type> <version>2.1.12-SNAPSHOT</version> <scope>provided</scope>
- </dependency> -->
- <!-- Commons -->
<dependency>
<groupId>org.argeo.commons</groupId>
<artifactId>org.argeo.util</artifactId>
<artifactId>org.argeo.security.core</artifactId>
<version>2.1.14-SNAPSHOT</version>
</dependency>
-
- <!-- Bridge Spring and OSGi -->
- <!-- <dependency> -->
- <!-- <groupId>org.argeo.tp</groupId> -->
- <!-- <artifactId>org.springframework.osgi.extender</artifactId> -->
- <!-- </dependency> -->
-
- <!-- Spring -->
- <!-- <dependency> -->
- <!-- <groupId>org.argeo.tp</groupId> -->
- <!-- <artifactId>org.springframework.context</artifactId> -->
- <!-- </dependency> -->
-
-
- <!-- Others -->
- <!-- <dependency> -->
- <!-- <groupId>org.argeo.tp</groupId> -->
- <!-- <artifactId>slf4j.org.apache.commons.logging</artifactId> -->
- <!-- </dependency> -->
</dependencies>
</project>
\ No newline at end of file
org.springframework.util,\
javax.jcr.security,\
org.apache.jackrabbit.*;resolution:=optional,\
+org.springframework.ldap.*;resolution:=optional,\
+org.springframework.security.ldap.*;resolution:=optional,\
*
<relativePath>..</relativePath>
</parent>
<artifactId>org.argeo.security.core</artifactId>
- <name>Commons Security Core</name>
+ <name>Commons Security</name>
<dependencies>
<dependency>
<groupId>org.argeo.commons</groupId>
<artifactId>org.argeo.server.jcr</artifactId>
<version>2.1.14-SNAPSHOT</version>
</dependency>
- <dependency>
- <groupId>org.argeo.commons</groupId>
- <artifactId>org.argeo.server.jackrabbit</artifactId>
- <version>2.1.14-SNAPSHOT</version>
- </dependency>
-
- <!-- SDK -->
-<!-- <dependency> -->
-<!-- <groupId>org.argeo.tp</groupId> -->
-<!-- <artifactId>argeo-tp</artifactId> -->
-<!-- <version>${version.argeo-distribution}</version> -->
-<!-- <scope>provided</scope> -->
-<!-- </dependency> -->
-
- <!-- Crypto -->
- <!-- <dependency> -->
- <!-- <groupId>org.argeo.tp</groupId> -->
- <!-- <artifactId>bcprov</artifactId> -->
- <!-- <optional>true</optional> -->
- <!-- </dependency> -->
- <!-- <dependency> -->
- <!-- <groupId>org.argeo.tp</groupId> -->
- <!-- <artifactId>org.apache.commons.codec</artifactId> -->
- <!-- </dependency> -->
-
- <!-- Spring -->
- <!-- <dependency> -->
- <!-- <groupId>org.argeo.tp</groupId> -->
- <!-- <artifactId>org.springframework.core</artifactId> -->
- <!-- </dependency> -->
- <!-- <dependency> -->
- <!-- <groupId>org.argeo.tp</groupId> -->
- <!-- <artifactId>org.springframework.beans</artifactId> -->
- <!-- </dependency> -->
- <!-- <dependency> -->
- <!-- <groupId>org.argeo.tp</groupId> -->
- <!-- <artifactId>org.springframework.context</artifactId> -->
- <!-- </dependency> -->
- <!-- <dependency> -->
- <!-- <groupId>org.argeo.tp</groupId> -->
- <!-- <artifactId>org.springframework.security.core</artifactId> -->
- <!-- </dependency> -->
- <!-- <dependency> -->
- <!-- <groupId>org.argeo.tp</groupId> -->
- <!-- <artifactId>org.springframework.transaction</artifactId> -->
- <!-- </dependency> -->
-
- <!-- OSGi -->
- <!-- <dependency> -->
- <!-- <groupId>org.argeo.tp</groupId> -->
- <!-- <artifactId>org.eclipse.osgi</artifactId> -->
- <!-- <scope>provided</scope> -->
- <!-- </dependency> -->
-
- <!-- Logging -->
- <!-- <dependency> -->
- <!-- <groupId>org.argeo.tp</groupId> -->
- <!-- <artifactId>slf4j.org.apache.commons.logging</artifactId> -->
- <!-- </dependency> -->
-
- <!-- <dependency> -->
- <!-- <groupId>org.argeo.tp</groupId> -->
- <!-- <artifactId>org.apache.log4j</artifactId> -->
- <!-- <optional>true</optional> -->
- <!-- </dependency> -->
-
- <!-- TEST -->
- <!-- <dependency> -->
- <!-- <groupId>org.argeo.tp</groupId> -->
- <!-- <artifactId>junit</artifactId> -->
- <!-- <scope>test</scope> -->
- <!-- </dependency> -->
- <!-- <dependency> -->
- <!-- <groupId>org.argeo.commons</groupId> -->
- <!-- <artifactId>org.argeo.dep.log4j</artifactId> -->
- <!-- <version>2.1.12-SNAPSHOT</version> -->
- <!-- <type>pom</type> -->
- <!-- <scope>test</scope> -->
- <!-- </dependency> -->
- <!-- <dependency> -->
- <!-- <groupId>org.argeo.commons</groupId> -->
- <!-- <artifactId>org.argeo.server.json</artifactId> -->
- <!-- <version>2.1.12-SNAPSHOT</version> -->
- <!-- <scope>test</scope> -->
- <!-- </dependency> -->
- <!-- <dependency> -->
- <!-- <groupId>org.argeo.tp</groupId> -->
- <!-- <artifactId>com.springsource.json</artifactId> -->
- <!-- <scope>test</scope> -->
- <!-- </dependency> -->
-
</dependencies>
</project>
\ No newline at end of file
--- /dev/null
+package org.argeo.security.core;
+
+import java.security.Principal;
+
+import org.osgi.service.useradmin.Authorization;
+
+/** Wraps an OSGi {@link Authorization} as a JAAS {@link Principal} */
+public final class AuthorizationPrincipal implements Principal {
+ private Authorization authorization;
+
+ public AuthorizationPrincipal(Authorization authorization) {
+ this.authorization = authorization;
+ }
+
+ @Override
+ public String getName() {
+ return authorization.getName();
+ }
+
+ public Authorization getAuthorization() {
+ return authorization;
+ }
+
+}
--- /dev/null
+/*
+ * Copyright (C) 2007-2012 Argeo GmbH
+ *
+ * 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 org.argeo.security.core;
+
+import java.util.Locale;
+import java.util.Map;
+
+import javax.security.auth.Subject;
+import javax.security.auth.callback.Callback;
+import javax.security.auth.callback.CallbackHandler;
+import javax.security.auth.callback.NameCallback;
+import javax.security.auth.callback.PasswordCallback;
+import javax.security.auth.login.LoginException;
+import javax.security.auth.spi.LoginModule;
+
+import org.argeo.jcr.ArgeoNames;
+import org.argeo.util.LocaleCallback;
+import org.argeo.util.LocaleUtils;
+import org.osgi.framework.BundleContext;
+import org.osgi.service.useradmin.Authorization;
+import org.osgi.service.useradmin.User;
+import org.osgi.service.useradmin.UserAdmin;
+import org.springframework.security.authentication.encoding.LdapShaPasswordEncoder;
+
+/** Login module which caches one subject per thread. */
+public class UserAdminLoginModule implements LoginModule {
+ // private final static Log log = LogFactory
+ // .getLog(UserAdminLoginModule.class);
+
+ private CallbackHandler callbackHandler;
+
+ private Subject subject;
+
+ private Long waitBetweenFailedLoginAttempts = 5 * 1000l;
+
+ /** Comma separated list of locales */
+ private String availableLocales = "";
+
+ private AuthorizationPrincipal auth = null;
+ private Locale selectedLocale = null;
+
+ private LdapShaPasswordEncoder shaPasswordEncoder = new LdapShaPasswordEncoder();
+
+ public UserAdminLoginModule() {
+
+ }
+
+ @SuppressWarnings("rawtypes")
+ public void initialize(Subject subject, CallbackHandler callbackHandler,
+ Map sharedState, Map options) {
+ this.callbackHandler = callbackHandler;
+ this.subject = subject;
+ }
+
+ public boolean login() throws LoginException {
+ try {
+ // TODO thread already logged in
+ // AuthorizationPrincipal principal = subject
+ // .getPrincipals(AuthorizationPrincipal.class).iterator();
+
+ if (callbackHandler == null)
+ throw new LoginException("No call back handler available");
+
+ // ask for username and password
+ NameCallback nameCallback = new NameCallback("User");
+ PasswordCallback passwordCallback = new PasswordCallback(
+ "Password", false);
+ LocaleCallback localeCallback = new LocaleCallback(availableLocales);
+ BundleContextCallback bundleContextCallback = new BundleContextCallback();
+
+ callbackHandler.handle(new Callback[] { nameCallback,
+ passwordCallback, localeCallback, bundleContextCallback });
+
+ selectedLocale = localeCallback.getSelectedLocale();
+
+ // create credentials
+ final String username = nameCallback.getName();
+ if (username == null || username.trim().equals(""))
+ return false;
+
+ char[] password = {};
+ if (passwordCallback.getPassword() != null)
+ password = passwordCallback.getPassword();
+
+ BundleContext bc = bundleContextCallback.getBundleContext();
+ UserAdmin userAdmin = bc.getService(bc
+ .getServiceReference(UserAdmin.class));
+
+ User user = (User) userAdmin.getRole(username);
+ // TODO use hash
+ boolean authenticated = user.hasCredential(
+ ArgeoNames.ARGEO_PASSWORD, new String(password));
+
+ if (!authenticated) {
+ // wait between failed login attempts
+ Thread.sleep(waitBetweenFailedLoginAttempts);
+ return false;
+ }
+
+ Authorization authorization = userAdmin.getAuthorization(user);
+ auth = new AuthorizationPrincipal(authorization);
+ return true;
+ } catch (LoginException e) {
+ throw e;
+ } catch (ThreadDeath e) {
+ LoginException le = new LoginException(
+ "Spring Security login thread died");
+ le.initCause(e);
+ throw le;
+ } catch (Exception e) {
+ LoginException le = new LoginException(
+ "Spring Security login failed");
+ le.initCause(e);
+ throw le;
+ }
+ }
+
+ @Override
+ public boolean logout() throws LoginException {
+ subject.getPrincipals(AuthorizationPrincipal.class).remove(auth);
+ return true;
+ }
+
+ @Override
+ public boolean commit() throws LoginException {
+ subject.getPrincipals().add(auth);
+ if (selectedLocale != null)
+ LocaleUtils.threadLocale.set(selectedLocale);
+ return true;
+ }
+
+ @Override
+ public boolean abort() throws LoginException {
+ auth = null;
+ selectedLocale = null;
+ return true;
+ }
+
+ public void setAvailableLocales(String locales) {
+ this.availableLocales = locales;
+ }
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (C) 2007-2012 Argeo GmbH
+ *
+ * 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 org.argeo.security.ldap;
+
+import org.springframework.security.authentication.encoding.LdapShaPasswordEncoder;
+
+/**
+ * {@link LdapShaPasswordEncoder} allowing to configure the usage of salt (APache
+ * Directory Server 1.0 does not support bind with SSHA)
+ */
+public class ArgeoLdapShaPasswordEncoder extends LdapShaPasswordEncoder {
+ private Boolean useSalt = true;
+
+ @Override
+ public String encodePassword(String rawPass, Object salt) {
+ return super.encodePassword(rawPass, useSalt ? salt : null);
+ }
+
+ public void setUseSalt(Boolean useSalt) {
+ this.useSalt = useSalt;
+ }
+
+}
--- /dev/null
+/*
+ * Copyright (C) 2007-2012 Argeo GmbH
+ *
+ * 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 org.argeo.security.ldap;
+
+import java.security.NoSuchAlgorithmException;
+import java.security.SecureRandom;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Random;
+import java.util.Set;
+import java.util.TreeSet;
+
+import org.argeo.ArgeoException;
+import org.argeo.security.UserAdminService;
+import org.springframework.ldap.core.ContextSource;
+import org.springframework.security.authentication.encoding.PasswordEncoder;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.GrantedAuthority;
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.security.core.userdetails.UserDetails;
+import org.springframework.security.ldap.userdetails.LdapUserDetailsManager;
+
+/** Extends {@link LdapUserDetailsManager} by adding password encoding support. */
+public class ArgeoLdapUserDetailsManager extends LdapUserDetailsManager
+ implements UserAdminService {
+ private String superUsername = "root";
+ private ArgeoUserAdminDaoLdap userAdminDao;
+ private PasswordEncoder passwordEncoder;
+ private final Random random;
+
+ public ArgeoLdapUserDetailsManager(ContextSource contextSource) {
+ super(contextSource);
+ this.random = createRandom();
+ }
+
+ private static Random createRandom() {
+ try {
+ return SecureRandom.getInstance("SHA1PRNG");
+ } catch (NoSuchAlgorithmException e) {
+ return new Random(System.currentTimeMillis());
+ }
+ }
+
+ @Override
+ public void changePassword(String oldPassword, String newPassword) {
+ Authentication authentication = SecurityContextHolder.getContext()
+ .getAuthentication();
+ if (authentication == null)
+ throw new ArgeoException(
+ "Cannot change password without authentication");
+ String username = authentication.getName();
+ UserDetails userDetails = loadUserByUsername(username);
+ String currentPassword = userDetails.getPassword();
+ if (currentPassword == null)
+ throw new ArgeoException("Cannot access current password");
+ if (!passwordEncoder
+ .isPasswordValid(currentPassword, oldPassword, null))
+ throw new ArgeoException("Old password invalid");
+ // Spring Security LDAP 2.0 is buggy when used with OpenLDAP and called
+ // with oldPassword argument
+ super.changePassword(null, encodePassword(newPassword));
+ }
+
+ public void newRole(String role) {
+ userAdminDao.createRole(role, superUsername);
+ }
+
+ public void synchronize() {
+ for (String username : userAdminDao.listUsers())
+ loadUserByUsername(username);
+ // TODO: find a way to remove from JCR
+ }
+
+ public void deleteRole(String role) {
+ userAdminDao.deleteRole(role);
+ }
+
+ public Set<String> listUsers() {
+ return userAdminDao.listUsers();
+ }
+
+ public Set<String> listUsersInRole(String role) {
+ Set<String> lst = new TreeSet<String>(
+ userAdminDao.listUsersInRole(role));
+ Iterator<String> it = lst.iterator();
+ while (it.hasNext()) {
+ if (it.next().equals(superUsername)) {
+ it.remove();
+ break;
+ }
+ }
+ return lst;
+ }
+
+ public List<String> listUserRoles(String username) {
+ UserDetails userDetails = loadUserByUsername(username);
+ List<String> roles = new ArrayList<String>();
+ for (GrantedAuthority ga : userDetails.getAuthorities()) {
+ roles.add(ga.getAuthority());
+ }
+ return Collections.unmodifiableList(roles);
+ }
+
+ public Set<String> listEditableRoles() {
+ return userAdminDao.listEditableRoles();
+ }
+
+ protected String encodePassword(String password) {
+ if (!password.startsWith("{")) {
+ byte[] salt = new byte[16];
+ random.nextBytes(salt);
+ return passwordEncoder.encodePassword(password, salt);
+ } else {
+ return password;
+ }
+ }
+
+ public void setPasswordEncoder(PasswordEncoder passwordEncoder) {
+ this.passwordEncoder = passwordEncoder;
+ }
+
+ public void setSuperUsername(String superUsername) {
+ this.superUsername = superUsername;
+ }
+
+ public void setUserAdminDao(ArgeoUserAdminDaoLdap userAdminDao) {
+ this.userAdminDao = userAdminDao;
+ }
+
+}
--- /dev/null
+/*
+ * Copyright (C) 2007-2012 Argeo GmbH
+ *
+ * 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 org.argeo.security.ldap;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+import java.util.TreeSet;
+
+import javax.naming.Name;
+import javax.naming.NamingException;
+import javax.naming.directory.DirContext;
+
+import org.springframework.ldap.core.ContextExecutor;
+import org.springframework.ldap.core.ContextMapper;
+import org.springframework.ldap.core.DirContextAdapter;
+import org.springframework.ldap.core.DistinguishedName;
+import org.springframework.ldap.core.LdapTemplate;
+import org.springframework.ldap.core.support.BaseLdapPathContextSource;
+import org.springframework.security.ldap.LdapUsernameToDnMapper;
+import org.springframework.security.ldap.LdapUtils;
+
+/**
+ * Wraps low-level LDAP operation on user and roles, used by
+ * {@link ArgeoLdapUserDetailsManager}
+ */
+public class ArgeoUserAdminDaoLdap {
+ private String userBase;
+ private String usernameAttribute;
+ private String groupBase;
+ private String[] groupClasses;
+
+ private String groupRoleAttribute;
+ private String groupMemberAttribute;
+ private String defaultRole;
+ private String rolePrefix;
+
+ private final LdapTemplate ldapTemplate;
+ private LdapUsernameToDnMapper usernameMapper;
+
+ /**
+ * Standard constructor, using the LDAP context source shared with Spring
+ * Security components.
+ */
+ public ArgeoUserAdminDaoLdap(BaseLdapPathContextSource contextSource) {
+ this.ldapTemplate = new LdapTemplate(contextSource);
+ }
+
+ @SuppressWarnings("unchecked")
+ public synchronized Set<String> listUsers() {
+ List<String> usernames = (List<String>) ldapTemplate.listBindings(
+ new DistinguishedName(userBase), new ContextMapper() {
+ public Object mapFromContext(Object ctxArg) {
+ DirContextAdapter ctx = (DirContextAdapter) ctxArg;
+ return ctx.getStringAttribute(usernameAttribute);
+ }
+ });
+
+ return Collections
+ .unmodifiableSortedSet(new TreeSet<String>(usernames));
+ }
+
+ @SuppressWarnings("unchecked")
+ public Set<String> listEditableRoles() {
+ return Collections.unmodifiableSortedSet(new TreeSet<String>(
+ ldapTemplate.listBindings(groupBase, new ContextMapper() {
+ public Object mapFromContext(Object ctxArg) {
+ String groupName = ((DirContextAdapter) ctxArg)
+ .getStringAttribute(groupRoleAttribute);
+ String roleName = convertGroupToRole(groupName);
+ return roleName;
+ }
+ })));
+ }
+
+ @SuppressWarnings("unchecked")
+ public Set<String> listUsersInRole(String role) {
+ return (Set<String>) ldapTemplate.lookup(
+ buildGroupDn(convertRoleToGroup(role)), new ContextMapper() {
+ public Object mapFromContext(Object ctxArg) {
+ DirContextAdapter ctx = (DirContextAdapter) ctxArg;
+ String[] userDns = ctx
+ .getStringAttributes(groupMemberAttribute);
+ TreeSet<String> set = new TreeSet<String>();
+ for (String userDn : userDns) {
+ DistinguishedName dn = new DistinguishedName(userDn);
+ String username = dn.getValue(usernameAttribute);
+ set.add(username);
+ }
+ return Collections.unmodifiableSortedSet(set);
+ }
+ });
+ }
+
+ public void createRole(String role, final String superuserName) {
+ String group = convertRoleToGroup(role);
+ DistinguishedName superuserDn = (DistinguishedName) ldapTemplate
+ .executeReadWrite(new ContextExecutor() {
+ public Object executeWithContext(DirContext ctx)
+ throws NamingException {
+ return LdapUtils.getFullDn(
+ usernameMapper.buildDn(superuserName), ctx);
+ }
+ });
+
+ Name groupDn = buildGroupDn(group);
+ DirContextAdapter context = new DirContextAdapter();
+ context.setAttributeValues("objectClass", groupClasses);
+ context.setAttributeValue("cn", group);
+ // Add superuser because cannot create empty group
+ context.setAttributeValue(groupMemberAttribute, superuserDn.toString());
+ ldapTemplate.bind(groupDn, context, null);
+ }
+
+ public void deleteRole(String role) {
+ String group = convertRoleToGroup(role);
+ Name dn = buildGroupDn(group);
+ ldapTemplate.unbind(dn);
+ }
+
+ /** Maps a role (ROLE_XXX) to the related LDAP group (xxx) */
+ protected String convertRoleToGroup(String role) {
+ String group = role;
+ if (group.startsWith(rolePrefix)) {
+ group = group.substring(rolePrefix.length());
+ group = group.toLowerCase();
+ }
+ return group;
+ }
+
+ /** Maps anLDAP group (xxx) to the related role (ROLE_XXX) */
+ protected String convertGroupToRole(String groupName) {
+ groupName = groupName.toUpperCase();
+
+ return rolePrefix + groupName;
+ }
+
+ protected Name buildGroupDn(String name) {
+ return new DistinguishedName(groupRoleAttribute + "=" + name + ","
+ + groupBase);
+ }
+
+ public void setUserBase(String userBase) {
+ this.userBase = userBase;
+ }
+
+ public void setUsernameAttribute(String usernameAttribute) {
+ this.usernameAttribute = usernameAttribute;
+ }
+
+ public void setGroupBase(String groupBase) {
+ this.groupBase = groupBase;
+ }
+
+ public void setGroupRoleAttribute(String groupRoleAttributeName) {
+ this.groupRoleAttribute = groupRoleAttributeName;
+ }
+
+ public void setGroupMemberAttribute(String groupMemberAttributeName) {
+ this.groupMemberAttribute = groupMemberAttributeName;
+ }
+
+ public void setDefaultRole(String defaultRole) {
+ this.defaultRole = defaultRole;
+ }
+
+ public void setRolePrefix(String rolePrefix) {
+ this.rolePrefix = rolePrefix;
+ }
+
+ public void setUsernameMapper(LdapUsernameToDnMapper usernameMapper) {
+ this.usernameMapper = usernameMapper;
+ }
+
+ public String getDefaultRole() {
+ return defaultRole;
+ }
+
+ public void setGroupClasses(String[] groupClasses) {
+ this.groupClasses = groupClasses;
+ }
+}
--- /dev/null
+/*
+ * Copyright (C) 2007-2012 Argeo GmbH
+ *
+ * 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 org.argeo.security.ldap.jcr;
+
+import java.security.NoSuchAlgorithmException;
+import java.security.SecureRandom;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Random;
+import java.util.SortedSet;
+
+import javax.jcr.Node;
+import javax.jcr.NodeIterator;
+import javax.jcr.Repository;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+import javax.jcr.query.Query;
+import javax.jcr.version.VersionManager;
+import javax.naming.Name;
+import javax.naming.directory.BasicAttribute;
+import javax.naming.directory.DirContext;
+import javax.naming.directory.ModificationItem;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.argeo.ArgeoException;
+import org.argeo.jcr.ArgeoNames;
+import org.argeo.jcr.ArgeoTypes;
+import org.argeo.jcr.JcrUtils;
+import org.argeo.security.SecurityUtils;
+import org.argeo.security.jcr.JcrSecurityModel;
+import org.argeo.security.jcr.JcrUserDetails;
+import org.argeo.security.jcr.SimpleJcrSecurityModel;
+import org.springframework.ldap.core.ContextMapper;
+import org.springframework.ldap.core.DirContextAdapter;
+import org.springframework.ldap.core.DirContextOperations;
+import org.springframework.ldap.core.DistinguishedName;
+import org.springframework.ldap.core.LdapTemplate;
+import org.springframework.security.authentication.encoding.PasswordEncoder;
+import org.springframework.security.core.GrantedAuthority;
+import org.springframework.security.core.userdetails.UserDetails;
+import org.springframework.security.ldap.LdapUsernameToDnMapper;
+import org.springframework.security.ldap.userdetails.UserDetailsContextMapper;
+
+/** Makes sure that LDAP and JCR are in line. */
+public class JcrLdapSynchronizer implements UserDetailsContextMapper,
+ ArgeoNames {
+ private final static Log log = LogFactory.getLog(JcrLdapSynchronizer.class);
+
+ // LDAP
+ private LdapTemplate ldapTemplate;
+ /**
+ * LDAP template whose context source has an object factory set to null. see
+ * <a href=
+ * "http://forum.springsource.org/showthread.php?55955-Persistent-search-with-spring-ldap"
+ * >this</a>
+ */
+ // private LdapTemplate rawLdapTemplate;
+
+ private String userBase;
+ private String usernameAttribute;
+ private String passwordAttribute;
+ private String[] userClasses;
+ // private String defaultUserRole ="ROLE_USER";
+
+ // private NamingListener ldapUserListener;
+ // private SearchControls subTreeSearchControls;
+ private LdapUsernameToDnMapper usernameMapper;
+
+ private PasswordEncoder passwordEncoder;
+ private final Random random;
+
+ // JCR
+ /** Admin session on the main workspace */
+ private Session nodeSession;
+ private Repository repository;
+
+ // private JcrProfileListener jcrProfileListener;
+ private JcrSecurityModel jcrSecurityModel = new SimpleJcrSecurityModel();
+
+ // Mapping
+ private Map<String, String> propertyToAttributes = new HashMap<String, String>();
+
+ public JcrLdapSynchronizer() {
+ random = createRandom();
+ }
+
+ public void init() {
+ try {
+ nodeSession = repository.login();
+
+ // TODO put this in a different thread, and poll the LDAP server
+ // until it is up
+ try {
+ synchronize();
+
+ // LDAP
+ // subTreeSearchControls = new SearchControls();
+ // subTreeSearchControls
+ // .setSearchScope(SearchControls.SUBTREE_SCOPE);
+ // LDAP listener
+ // ldapUserListener = new LdapUserListener();
+ // rawLdapTemplate.executeReadOnly(new ContextExecutor() {
+ // public Object executeWithContext(DirContext ctx)
+ // throws NamingException {
+ // EventDirContext ectx = (EventDirContext) ctx.lookup("");
+ // ectx.addNamingListener(userBase, "("
+ // + usernameAttribute + "=*)",
+ // subTreeSearchControls, ldapUserListener);
+ // return null;
+ // }
+ // });
+ } catch (Exception e) {
+ log.error("Could not synchronize and listen to LDAP,"
+ + " probably because the LDAP server is not available."
+ + " Restart the system as soon as possible.", e);
+ }
+
+ // JCR
+ // String[] nodeTypes = { ArgeoTypes.ARGEO_USER_PROFILE };
+ // jcrProfileListener = new JcrProfileListener();
+ // noLocal is used so that we are not notified when we modify JCR
+ // from LDAP
+ // nodeSession
+ // .getWorkspace()
+ // .getObservationManager()
+ // .addEventListener(jcrProfileListener,
+ // Event.PROPERTY_CHANGED | Event.NODE_ADDED, "/",
+ // true, null, nodeTypes, true);
+ } catch (Exception e) {
+ JcrUtils.logoutQuietly(nodeSession);
+ throw new ArgeoException("Cannot initialize LDAP/JCR synchronizer",
+ e);
+ }
+ }
+
+ public void destroy() {
+ // JcrUtils.removeListenerQuietly(nodeSession, jcrProfileListener);
+ JcrUtils.logoutQuietly(nodeSession);
+ // try {
+ // rawLdapTemplate.executeReadOnly(new ContextExecutor() {
+ // public Object executeWithContext(DirContext ctx)
+ // throws NamingException {
+ // EventDirContext ectx = (EventDirContext) ctx.lookup("");
+ // ectx.removeNamingListener(ldapUserListener);
+ // return null;
+ // }
+ // });
+ // } catch (Exception e) {
+ // // silent (LDAP server may have been shutdown already)
+ // if (log.isTraceEnabled())
+ // log.trace("Cannot remove LDAP listener", e);
+ // }
+ }
+
+ /*
+ * LDAP TO JCR
+ */
+ /** Full synchronization between LDAP and JCR. LDAP has priority. */
+ protected void synchronize() {
+ try {
+ Name userBaseName = new DistinguishedName(userBase);
+ // TODO subtree search?
+ @SuppressWarnings("unchecked")
+ List<String> userPaths = (List<String>) ldapTemplate.listBindings(
+ userBaseName, new ContextMapper() {
+ public Object mapFromContext(Object ctxObj) {
+ try {
+ return mapLdapToJcr((DirContextAdapter) ctxObj);
+ } catch (Exception e) {
+ // do not break process because of error
+ log.error(
+ "Could not LDAP->JCR synchronize user "
+ + ctxObj, e);
+ return null;
+ }
+ }
+ });
+
+ // create accounts which are not in LDAP
+ Query query = nodeSession
+ .getWorkspace()
+ .getQueryManager()
+ .createQuery(
+ "select * from [" + ArgeoTypes.ARGEO_USER_PROFILE
+ + "]", Query.JCR_SQL2);
+ NodeIterator it = query.execute().getNodes();
+ while (it.hasNext()) {
+ Node userProfile = it.nextNode();
+ String path = userProfile.getPath();
+ try {
+ if (!userPaths.contains(path)) {
+ String username = userProfile
+ .getProperty(ARGEO_USER_ID).getString();
+ // GrantedAuthority[] authorities = {new
+ // GrantedAuthorityImpl(defaultUserRole)};
+ List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();
+ JcrUserDetails userDetails = new JcrUserDetails(
+ userProfile, username, authorities);
+ String dn = createLdapUser(userDetails);
+ log.warn("Created ldap entry '" + dn + "' for user '"
+ + username + "'");
+
+ // if(!userProfile.getProperty(ARGEO_ENABLED).getBoolean()){
+ // continue profiles;
+ // }
+ //
+ // log.warn("Path "
+ // + path
+ // + " not found in LDAP, disabling user "
+ // + userProfile.getProperty(ArgeoNames.ARGEO_USER_ID)
+ // .getString());
+
+ // Temporary hack to repair previous behaviour
+ if (!userProfile.getProperty(ARGEO_ENABLED)
+ .getBoolean()) {
+ VersionManager versionManager = nodeSession
+ .getWorkspace().getVersionManager();
+ versionManager.checkout(userProfile.getPath());
+ userProfile.setProperty(ArgeoNames.ARGEO_ENABLED,
+ true);
+ nodeSession.save();
+ versionManager.checkin(userProfile.getPath());
+ }
+ }
+ } catch (Exception e) {
+ log.error("Cannot process " + path, e);
+ }
+ }
+ } catch (Exception e) {
+ JcrUtils.discardQuietly(nodeSession);
+ log.error("Cannot synchronize LDAP and JCR", e);
+ // throw new ArgeoException("Cannot synchronize LDAP and JCR", e);
+ }
+ }
+
+ private String createLdapUser(UserDetails user) {
+ DirContextAdapter ctx = new DirContextAdapter();
+ mapUserToContext(user, ctx);
+ DistinguishedName dn = usernameMapper.buildDn(user.getUsername());
+ ldapTemplate.bind(dn, ctx, null);
+ return dn.toString();
+ }
+
+ /** Called during authentication in order to retrieve user details */
+ public UserDetails mapUserFromContext(final DirContextOperations ctx,
+ final String username,
+ Collection<? extends GrantedAuthority> authorities) {
+ if (ctx == null)
+ throw new ArgeoException("No LDAP information for user " + username);
+
+ String ldapUsername = ctx.getStringAttribute(usernameAttribute);
+ if (!ldapUsername.equals(username))
+ throw new ArgeoException("Logged in with username " + username
+ + " but LDAP user is " + ldapUsername);
+
+ Node userProfile = jcrSecurityModel.sync(nodeSession, username,
+ SecurityUtils.authoritiesToStringList(authorities));
+ // JcrUserDetails.checkAccountStatus(userProfile);
+
+ // password
+ SortedSet<?> passwordAttributes = ctx
+ .getAttributeSortedStringSet(passwordAttribute);
+ String password;
+ if (passwordAttributes == null || passwordAttributes.size() == 0) {
+ // throw new ArgeoException("No password found for user " +
+ // username);
+ password = "NULL";
+ } else {
+ byte[] arr = (byte[]) passwordAttributes.first();
+ password = new String(arr);
+ // erase password
+ Arrays.fill(arr, (byte) 0);
+ }
+
+ try {
+ return new JcrUserDetails(userProfile, password, authorities);
+ } catch (RepositoryException e) {
+ throw new ArgeoException("Cannot retrieve user details for "
+ + username, e);
+ }
+ }
+
+ /**
+ * Writes an LDAP context to the JCR user profile.
+ *
+ * @return path to user profile
+ */
+ protected synchronized String mapLdapToJcr(DirContextAdapter ctx) {
+ Session session = nodeSession;
+ try {
+ // process
+ String username = ctx.getStringAttribute(usernameAttribute);
+
+ Node userProfile = jcrSecurityModel.sync(session, username, null);
+ Map<String, String> modifications = new HashMap<String, String>();
+ for (String jcrProperty : propertyToAttributes.keySet())
+ ldapToJcr(userProfile, jcrProperty, ctx, modifications);
+
+ int modifCount = modifications.size();
+ if (modifCount > 0) {
+ session.getWorkspace().getVersionManager()
+ .checkout(userProfile.getPath());
+ for (String prop : modifications.keySet())
+ userProfile.setProperty(prop, modifications.get(prop));
+ JcrUtils.updateLastModified(userProfile);
+ session.save();
+ session.getWorkspace().getVersionManager()
+ .checkin(userProfile.getPath());
+ if (log.isDebugEnabled())
+ log.debug("Mapped " + modifCount + " LDAP modification"
+ + (modifCount == 1 ? "" : "s") + " from "
+ + ctx.getDn() + " to " + userProfile);
+ }
+ return userProfile.getPath();
+ } catch (Exception e) {
+ JcrUtils.discardQuietly(session);
+ throw new ArgeoException("Cannot synchronize JCR and LDAP", e);
+ }
+ }
+
+ /** Maps an LDAP property to a JCR property */
+ protected void ldapToJcr(Node userProfile, String jcrProperty,
+ DirContextOperations ctx, Map<String, String> modifications) {
+ // TODO do we really need DirContextOperations?
+ try {
+ String ldapAttribute;
+ if (propertyToAttributes.containsKey(jcrProperty))
+ ldapAttribute = propertyToAttributes.get(jcrProperty);
+ else
+ throw new ArgeoException(
+ "No LDAP attribute mapped for JCR proprty "
+ + jcrProperty);
+
+ String value = ctx.getStringAttribute(ldapAttribute);
+ String jcrValue = userProfile.hasProperty(jcrProperty) ? userProfile
+ .getProperty(jcrProperty).getString() : null;
+ if (value != null && jcrValue != null) {
+ if (!value.equals(jcrValue))
+ modifications.put(jcrProperty, value);
+ } else if (value != null && jcrValue == null) {
+ modifications.put(jcrProperty, value);
+ } else if (value == null && jcrValue != null) {
+ modifications.put(jcrProperty, value);
+ }
+ } catch (Exception e) {
+ throw new ArgeoException("Cannot map JCR property " + jcrProperty
+ + " from LDAP", e);
+ }
+ }
+
+ /*
+ * JCR to LDAP
+ */
+
+ public void mapUserToContext(UserDetails user, final DirContextAdapter ctx) {
+ if (!(user instanceof JcrUserDetails))
+ throw new ArgeoException("Unsupported user details: "
+ + user.getClass());
+
+ ctx.setAttributeValues("objectClass", userClasses);
+ ctx.setAttributeValue(usernameAttribute, user.getUsername());
+ ctx.setAttributeValue(passwordAttribute,
+ encodePassword(user.getPassword()));
+
+ final JcrUserDetails jcrUserDetails = (JcrUserDetails) user;
+ try {
+ Node userProfile = nodeSession
+ .getNode(jcrUserDetails.getHomePath()).getNode(
+ ARGEO_PROFILE);
+ for (String jcrProperty : propertyToAttributes.keySet()) {
+ if (userProfile.hasProperty(jcrProperty)) {
+ ModificationItem mi = jcrToLdap(jcrProperty, userProfile
+ .getProperty(jcrProperty).getString());
+ if (mi != null)
+ ctx.setAttribute(mi.getAttribute());
+ }
+ }
+ if (log.isTraceEnabled())
+ log.trace("Mapped " + userProfile + " to " + ctx.getDn());
+ } catch (RepositoryException e) {
+ throw new ArgeoException("Cannot synchronize JCR and LDAP", e);
+ }
+
+ }
+
+ /** Maps a JCR property to an LDAP property */
+ protected ModificationItem jcrToLdap(String jcrProperty, String value) {
+ // TODO do we really need DirContextOperations?
+ try {
+ String ldapAttribute;
+ if (propertyToAttributes.containsKey(jcrProperty))
+ ldapAttribute = propertyToAttributes.get(jcrProperty);
+ else
+ return null;
+
+ // fix issue with empty 'sn' in LDAP
+ if (ldapAttribute.equals("sn") && (value.trim().equals("")))
+ return null;
+ // fix issue with empty 'description' in LDAP
+ if (ldapAttribute.equals("description") && value.trim().equals(""))
+ return null;
+ BasicAttribute attr = new BasicAttribute(
+ propertyToAttributes.get(jcrProperty), value);
+ ModificationItem mi = new ModificationItem(
+ DirContext.REPLACE_ATTRIBUTE, attr);
+ return mi;
+ } catch (Exception e) {
+ throw new ArgeoException("Cannot map JCR property " + jcrProperty
+ + " from LDAP", e);
+ }
+ }
+
+ /*
+ * UTILITIES
+ */
+ protected String encodePassword(String password) {
+ if (!password.startsWith("{")) {
+ byte[] salt = new byte[16];
+ random.nextBytes(salt);
+ return passwordEncoder.encodePassword(password, salt);
+ } else {
+ return password;
+ }
+ }
+
+ private static Random createRandom() {
+ try {
+ return SecureRandom.getInstance("SHA1PRNG");
+ } catch (NoSuchAlgorithmException e) {
+ return new Random(System.currentTimeMillis());
+ }
+ }
+
+ /*
+ * DEPENDENCY INJECTION
+ */
+
+ public void setLdapTemplate(LdapTemplate ldapTemplate) {
+ this.ldapTemplate = ldapTemplate;
+ }
+
+ public void setRawLdapTemplate(LdapTemplate rawLdapTemplate) {
+ // this.rawLdapTemplate = rawLdapTemplate;
+ }
+
+ public void setRepository(Repository repository) {
+ this.repository = repository;
+ }
+
+ public void setUserBase(String userBase) {
+ this.userBase = userBase;
+ }
+
+ public void setUsernameAttribute(String usernameAttribute) {
+ this.usernameAttribute = usernameAttribute;
+ }
+
+ public void setPropertyToAttributes(Map<String, String> propertyToAttributes) {
+ this.propertyToAttributes = propertyToAttributes;
+ }
+
+ public void setUsernameMapper(LdapUsernameToDnMapper usernameMapper) {
+ this.usernameMapper = usernameMapper;
+ }
+
+ public void setPasswordAttribute(String passwordAttribute) {
+ this.passwordAttribute = passwordAttribute;
+ }
+
+ public void setUserClasses(String[] userClasses) {
+ this.userClasses = userClasses;
+ }
+
+ public void setPasswordEncoder(PasswordEncoder passwordEncoder) {
+ this.passwordEncoder = passwordEncoder;
+ }
+
+ public void setJcrSecurityModel(JcrSecurityModel jcrSecurityModel) {
+ this.jcrSecurityModel = jcrSecurityModel;
+ }
+
+ /** Listen to LDAP */
+ // class LdapUserListener implements ObjectChangeListener,
+ // NamespaceChangeListener, UnsolicitedNotificationListener {
+ //
+ // public void namingExceptionThrown(NamingExceptionEvent evt) {
+ // evt.getException().printStackTrace();
+ // }
+ //
+ // public void objectChanged(NamingEvent evt) {
+ // Binding user = evt.getNewBinding();
+ // // TODO find a way not to be called when JCR is the source of the
+ // // modification
+ // DirContextAdapter ctx = (DirContextAdapter) ldapTemplate
+ // .lookup(user.getName());
+ // mapLdapToJcr(ctx);
+ // }
+ //
+ // public void objectAdded(NamingEvent evt) {
+ // Binding user = evt.getNewBinding();
+ // DirContextAdapter ctx = (DirContextAdapter) ldapTemplate
+ // .lookup(user.getName());
+ // mapLdapToJcr(ctx);
+ // }
+ //
+ // public void objectRemoved(NamingEvent evt) {
+ // if (log.isDebugEnabled())
+ // log.debug(evt);
+ // }
+ //
+ // public void objectRenamed(NamingEvent evt) {
+ // if (log.isDebugEnabled())
+ // log.debug(evt);
+ // }
+ //
+ // public void notificationReceived(UnsolicitedNotificationEvent evt) {
+ // UnsolicitedNotification notification = evt.getNotification();
+ // NamingException ne = notification.getException();
+ // String msg = "LDAP notification " + "ID=" + notification.getID()
+ // + ", referrals=" + notification.getReferrals();
+ // if (ne != null) {
+ // if (log.isTraceEnabled())
+ // log.trace(msg + ", exception= " + ne, ne);
+ // else
+ // log.warn(msg + ", exception= " + ne);
+ // } else if (log.isDebugEnabled()) {
+ // log.debug("Unsollicited LDAP notification " + msg);
+ // }
+ // }
+ //
+ // }
+
+ /** Listen to JCR */
+ // class JcrProfileListener implements EventListener {
+ //
+ // public void onEvent(EventIterator events) {
+ // try {
+ // final Map<Name, List<ModificationItem>> modifications = new HashMap<Name,
+ // List<ModificationItem>>();
+ // while (events.hasNext()) {
+ // Event event = events.nextEvent();
+ // try {
+ // if (Event.PROPERTY_CHANGED == event.getType()) {
+ // Property property = (Property) nodeSession
+ // .getItem(event.getPath());
+ // String propertyName = property.getName();
+ // Node userProfile = property.getParent();
+ // String username = userProfile.getProperty(
+ // ARGEO_USER_ID).getString();
+ // if (propertyToAttributes.containsKey(propertyName)) {
+ // Name name = usernameMapper.buildDn(username);
+ // if (!modifications.containsKey(name))
+ // modifications.put(name,
+ // new ArrayList<ModificationItem>());
+ // String value = property.getString();
+ // ModificationItem mi = jcrToLdap(propertyName,
+ // value);
+ // if (mi != null)
+ // modifications.get(name).add(mi);
+ // }
+ // } else if (Event.NODE_ADDED == event.getType()) {
+ // Node userProfile = nodeSession.getNode(event
+ // .getPath());
+ // String username = userProfile.getProperty(
+ // ARGEO_USER_ID).getString();
+ // Name name = usernameMapper.buildDn(username);
+ // for (String propertyName : propertyToAttributes
+ // .keySet()) {
+ // if (!modifications.containsKey(name))
+ // modifications.put(name,
+ // new ArrayList<ModificationItem>());
+ // String value = userProfile.getProperty(
+ // propertyName).getString();
+ // ModificationItem mi = jcrToLdap(propertyName,
+ // value);
+ // if (mi != null)
+ // modifications.get(name).add(mi);
+ // }
+ // }
+ // } catch (RepositoryException e) {
+ // throw new ArgeoException("Cannot process event "
+ // + event, e);
+ // }
+ // }
+ //
+ // for (Name name : modifications.keySet()) {
+ // List<ModificationItem> userModifs = modifications.get(name);
+ // int modifCount = userModifs.size();
+ // ldapTemplate.modifyAttributes(name, userModifs
+ // .toArray(new ModificationItem[modifCount]));
+ // if (log.isDebugEnabled())
+ // log.debug("Mapped " + modifCount + " JCR modification"
+ // + (modifCount == 1 ? "" : "s") + " to " + name);
+ // }
+ // } catch (Exception e) {
+ // // if (log.isDebugEnabled())
+ // // e.printStackTrace();
+ // throw new ArgeoException("Cannot process JCR events ("
+ // + e.getMessage() + ")", e);
+ // }
+ // }
+ //
+ // }
+}
--- /dev/null
+/*
+ * Copyright (C) 2007-2012 Argeo GmbH
+ *
+ * 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 org.argeo.security.ldap.jcr;
+
+import java.util.Collection;
+import java.util.UUID;
+
+import javax.jcr.Node;
+import javax.jcr.Repository;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+
+import org.argeo.ArgeoException;
+import org.argeo.jcr.ArgeoNames;
+import org.argeo.jcr.JcrUtils;
+import org.argeo.jcr.UserJcrUtils;
+import org.argeo.security.jcr.JcrUserDetails;
+import org.springframework.ldap.core.DirContextAdapter;
+import org.springframework.ldap.core.DirContextOperations;
+import org.springframework.security.core.GrantedAuthority;
+import org.springframework.security.core.userdetails.UserDetails;
+import org.springframework.security.ldap.userdetails.UserDetailsContextMapper;
+
+/** @deprecated Read only mapping from LDAP to user details */
+@Deprecated
+public class JcrUserDetailsContextMapper implements UserDetailsContextMapper,
+ ArgeoNames {
+ /** Admin session on the security workspace */
+ private Session securitySession;
+ private Repository repository;
+ private String securityWorkspace = "security";
+
+ public void init() {
+ try {
+ securitySession = repository.login(securityWorkspace);
+ } catch (RepositoryException e) {
+ JcrUtils.logoutQuietly(securitySession);
+ throw new ArgeoException(
+ "Cannot initialize LDAP/JCR user details context mapper", e);
+ }
+ }
+
+ public void destroy() {
+ JcrUtils.logoutQuietly(securitySession);
+ }
+
+ /** Called during authentication in order to retrieve user details */
+ public UserDetails mapUserFromContext(final DirContextOperations ctx,
+ final String username,
+ Collection<? extends GrantedAuthority> authorities) {
+ if (ctx == null)
+ throw new ArgeoException("No LDAP information for user " + username);
+ Node userHome = UserJcrUtils.getUserHome(securitySession, username);
+ if (userHome == null)
+ throw new ArgeoException("No JCR information for user " + username);
+
+ // password
+ // SortedSet<?> passwordAttributes = ctx
+ // .getAttributeSortedStringSet(passwordAttribute);
+ // String password;
+ // if (passwordAttributes == null || passwordAttributes.size() == 0) {
+ // throw new ArgeoException("No password found for user " + username);
+ // } else {
+ // byte[] arr = (byte[]) passwordAttributes.first();
+ // password = new String(arr);
+ // // erase password
+ // Arrays.fill(arr, (byte) 0);
+ // }
+
+ try {
+ // we don't have access to password, so let's not pretend
+ String password = UUID.randomUUID().toString();
+ return new JcrUserDetails(userHome.getNode(ARGEO_PROFILE),
+ password, authorities);
+ } catch (RepositoryException e) {
+ throw new ArgeoException("Cannot retrieve user details for "
+ + username, e);
+ }
+ }
+
+ public void mapUserToContext(UserDetails user, final DirContextAdapter ctx) {
+ throw new UnsupportedOperationException("LDAP access is read-only");
+ }
+
+}
<relativePath>..</relativePath>
</parent>
<artifactId>org.argeo.security.jackrabbit</artifactId>
- <name>Commons Security Jackrabbit</name>
+ <name>Commons Jackrabbit Extensions</name>
<dependencies>
<dependency>
<groupId>org.argeo.commons</groupId>
<artifactId>org.argeo.security.core</artifactId>
<version>2.1.14-SNAPSHOT</version>
</dependency>
-<!-- <dependency> -->
-<!-- <groupId>org.argeo.tp</groupId> -->
-<!-- <artifactId>javax.jcr</artifactId> -->
-<!-- </dependency> -->
-<!-- <dependency> -->
-<!-- <groupId>org.argeo.tp</groupId> -->
-<!-- <artifactId>org.apache.jackrabbit</artifactId> -->
-<!-- </dependency> -->
-<!-- <dependency> -->
-<!-- <groupId>org.argeo.tp</groupId> -->
-<!-- <artifactId>org.springframework.security.core</artifactId> -->
-<!-- </dependency> -->
</dependencies>
</project>
\ No newline at end of file
+++ /dev/null
-<?xml version="1.0" encoding="UTF-8"?>
-<classpath>
- <classpathentry kind="src" path="src" />
- <classpathentry kind="con"
- path="org.eclipse.pde.core.requiredPlugins" />
- <classpathentry kind="con"
- path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6" />
- <classpathentry kind="output" path="bin" />
-</classpath>
+++ /dev/null
-<?xml version="1.0" encoding="UTF-8"?>
-<projectDescription>
- <name>org.argeo.security.ldap</name>
- <comment></comment>
- <projects>
- </projects>
- <buildSpec>
- <buildCommand>
- <name>org.eclipse.jdt.core.javabuilder</name>
- <arguments>
- </arguments>
- </buildCommand>
- <buildCommand>
- <name>org.eclipse.pde.ManifestBuilder</name>
- <arguments>
- </arguments>
- </buildCommand>
- <buildCommand>
- <name>org.eclipse.pde.SchemaBuilder</name>
- <arguments>
- </arguments>
- </buildCommand>
- </buildSpec>
- <natures>
- <nature>org.eclipse.pde.PluginNature</nature>
- <nature>org.eclipse.jdt.core.javanature</nature>
- </natures>
-</projectDescription>
+++ /dev/null
-Import-Package: org.springframework.core,\
-org.springframework.dao,\
-javax.jcr.nodetype,\
-*
\ No newline at end of file
+++ /dev/null
-source.. = src/
+++ /dev/null
-<?xml version="1.0" encoding="UTF-8" standalone="no"?><project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
- <modelVersion>4.0.0</modelVersion>
- <parent>
- <groupId>org.argeo.commons</groupId>
- <artifactId>argeo-commons</artifactId>
- <version>2.1.14-SNAPSHOT</version>
- <relativePath>..</relativePath>
- </parent>
- <artifactId>org.argeo.security.ldap</artifactId>
- <name>Commons Security LDAP</name>
- <build>
- <plugins>
- <plugin>
- <groupId>org.apache.maven.plugins</groupId>
- <artifactId>maven-compiler-plugin</artifactId>
- </plugin>
- <plugin>
- <groupId>org.apache.maven.plugins</groupId>
- <artifactId>maven-source-plugin</artifactId>
- </plugin>
- <plugin>
- <groupId>org.apache.maven.plugins</groupId>
- <artifactId>maven-jar-plugin</artifactId>
- </plugin>
- <plugin>
- <groupId>org.apache.felix</groupId>
- <artifactId>maven-bundle-plugin</artifactId>
-
- <configuration>
- <instructions>
- <Export-Package>
- org.argeo.security.ldap.*
- </Export-Package>
- <Import-Package>
- org.springframework.core,
- org.springframework.dao,
- javax.jcr.nodetype,
- *
- </Import-Package>
- </instructions>
- </configuration>
- </plugin>
- </plugins>
- </build>
- <dependencies>
- <dependency>
- <groupId>org.argeo.commons</groupId>
- <artifactId>org.argeo.util</artifactId>
- <version>2.1.14-SNAPSHOT</version>
- </dependency>
- <dependency>
- <groupId>org.argeo.commons</groupId>
- <artifactId>org.argeo.security.core</artifactId>
- <version>2.1.14-SNAPSHOT</version>
- </dependency>
-
- <!-- JCR -->
-<!-- <dependency> -->
-<!-- <groupId>org.argeo.tp</groupId> -->
-<!-- <artifactId>javax.jcr</artifactId> -->
-<!-- </dependency> -->
-
- <!-- Spring -->
-<!-- <dependency> -->
-<!-- <groupId>org.argeo.tp</groupId> -->
-<!-- <artifactId>org.springframework.ldap</artifactId> -->
-<!-- </dependency> -->
-
- <!-- Logging -->
-<!-- <dependency> -->
-<!-- <groupId>org.argeo.tp</groupId> -->
-<!-- <artifactId>slf4j.org.apache.commons.logging</artifactId> -->
-<!-- </dependency> -->
-
- </dependencies>
-</project>
\ No newline at end of file
+++ /dev/null
-/*
- * Copyright (C) 2007-2012 Argeo GmbH
- *
- * 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 org.argeo.security.ldap;
-
-import org.springframework.security.authentication.encoding.LdapShaPasswordEncoder;
-
-/**
- * {@link LdapShaPasswordEncoder} allowing to configure the usage of salt (APache
- * Directory Server 1.0 does not support bind with SSHA)
- */
-public class ArgeoLdapShaPasswordEncoder extends LdapShaPasswordEncoder {
- private Boolean useSalt = true;
-
- @Override
- public String encodePassword(String rawPass, Object salt) {
- return super.encodePassword(rawPass, useSalt ? salt : null);
- }
-
- public void setUseSalt(Boolean useSalt) {
- this.useSalt = useSalt;
- }
-
-}
+++ /dev/null
-/*
- * Copyright (C) 2007-2012 Argeo GmbH
- *
- * 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 org.argeo.security.ldap;
-
-import java.security.NoSuchAlgorithmException;
-import java.security.SecureRandom;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Random;
-import java.util.Set;
-import java.util.TreeSet;
-
-import org.argeo.ArgeoException;
-import org.argeo.security.UserAdminService;
-import org.springframework.ldap.core.ContextSource;
-import org.springframework.security.authentication.encoding.PasswordEncoder;
-import org.springframework.security.core.Authentication;
-import org.springframework.security.core.GrantedAuthority;
-import org.springframework.security.core.context.SecurityContextHolder;
-import org.springframework.security.core.userdetails.UserDetails;
-import org.springframework.security.ldap.userdetails.LdapUserDetailsManager;
-
-/** Extends {@link LdapUserDetailsManager} by adding password encoding support. */
-public class ArgeoLdapUserDetailsManager extends LdapUserDetailsManager
- implements UserAdminService {
- private String superUsername = "root";
- private ArgeoUserAdminDaoLdap userAdminDao;
- private PasswordEncoder passwordEncoder;
- private final Random random;
-
- public ArgeoLdapUserDetailsManager(ContextSource contextSource) {
- super(contextSource);
- this.random = createRandom();
- }
-
- private static Random createRandom() {
- try {
- return SecureRandom.getInstance("SHA1PRNG");
- } catch (NoSuchAlgorithmException e) {
- return new Random(System.currentTimeMillis());
- }
- }
-
- @Override
- public void changePassword(String oldPassword, String newPassword) {
- Authentication authentication = SecurityContextHolder.getContext()
- .getAuthentication();
- if (authentication == null)
- throw new ArgeoException(
- "Cannot change password without authentication");
- String username = authentication.getName();
- UserDetails userDetails = loadUserByUsername(username);
- String currentPassword = userDetails.getPassword();
- if (currentPassword == null)
- throw new ArgeoException("Cannot access current password");
- if (!passwordEncoder
- .isPasswordValid(currentPassword, oldPassword, null))
- throw new ArgeoException("Old password invalid");
- // Spring Security LDAP 2.0 is buggy when used with OpenLDAP and called
- // with oldPassword argument
- super.changePassword(null, encodePassword(newPassword));
- }
-
- public void newRole(String role) {
- userAdminDao.createRole(role, superUsername);
- }
-
- public void synchronize() {
- for (String username : userAdminDao.listUsers())
- loadUserByUsername(username);
- // TODO: find a way to remove from JCR
- }
-
- public void deleteRole(String role) {
- userAdminDao.deleteRole(role);
- }
-
- public Set<String> listUsers() {
- return userAdminDao.listUsers();
- }
-
- public Set<String> listUsersInRole(String role) {
- Set<String> lst = new TreeSet<String>(
- userAdminDao.listUsersInRole(role));
- Iterator<String> it = lst.iterator();
- while (it.hasNext()) {
- if (it.next().equals(superUsername)) {
- it.remove();
- break;
- }
- }
- return lst;
- }
-
- public List<String> listUserRoles(String username) {
- UserDetails userDetails = loadUserByUsername(username);
- List<String> roles = new ArrayList<String>();
- for (GrantedAuthority ga : userDetails.getAuthorities()) {
- roles.add(ga.getAuthority());
- }
- return Collections.unmodifiableList(roles);
- }
-
- public Set<String> listEditableRoles() {
- return userAdminDao.listEditableRoles();
- }
-
- protected String encodePassword(String password) {
- if (!password.startsWith("{")) {
- byte[] salt = new byte[16];
- random.nextBytes(salt);
- return passwordEncoder.encodePassword(password, salt);
- } else {
- return password;
- }
- }
-
- public void setPasswordEncoder(PasswordEncoder passwordEncoder) {
- this.passwordEncoder = passwordEncoder;
- }
-
- public void setSuperUsername(String superUsername) {
- this.superUsername = superUsername;
- }
-
- public void setUserAdminDao(ArgeoUserAdminDaoLdap userAdminDao) {
- this.userAdminDao = userAdminDao;
- }
-
-}
+++ /dev/null
-/*
- * Copyright (C) 2007-2012 Argeo GmbH
- *
- * 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 org.argeo.security.ldap;
-
-import java.util.Collections;
-import java.util.List;
-import java.util.Set;
-import java.util.TreeSet;
-
-import javax.naming.Name;
-import javax.naming.NamingException;
-import javax.naming.directory.DirContext;
-
-import org.springframework.ldap.core.ContextExecutor;
-import org.springframework.ldap.core.ContextMapper;
-import org.springframework.ldap.core.DirContextAdapter;
-import org.springframework.ldap.core.DistinguishedName;
-import org.springframework.ldap.core.LdapTemplate;
-import org.springframework.ldap.core.support.BaseLdapPathContextSource;
-import org.springframework.security.ldap.LdapUsernameToDnMapper;
-import org.springframework.security.ldap.LdapUtils;
-
-/**
- * Wraps low-level LDAP operation on user and roles, used by
- * {@link ArgeoLdapUserDetailsManager}
- */
-public class ArgeoUserAdminDaoLdap {
- private String userBase;
- private String usernameAttribute;
- private String groupBase;
- private String[] groupClasses;
-
- private String groupRoleAttribute;
- private String groupMemberAttribute;
- private String defaultRole;
- private String rolePrefix;
-
- private final LdapTemplate ldapTemplate;
- private LdapUsernameToDnMapper usernameMapper;
-
- /**
- * Standard constructor, using the LDAP context source shared with Spring
- * Security components.
- */
- public ArgeoUserAdminDaoLdap(BaseLdapPathContextSource contextSource) {
- this.ldapTemplate = new LdapTemplate(contextSource);
- }
-
- @SuppressWarnings("unchecked")
- public synchronized Set<String> listUsers() {
- List<String> usernames = (List<String>) ldapTemplate.listBindings(
- new DistinguishedName(userBase), new ContextMapper() {
- public Object mapFromContext(Object ctxArg) {
- DirContextAdapter ctx = (DirContextAdapter) ctxArg;
- return ctx.getStringAttribute(usernameAttribute);
- }
- });
-
- return Collections
- .unmodifiableSortedSet(new TreeSet<String>(usernames));
- }
-
- @SuppressWarnings("unchecked")
- public Set<String> listEditableRoles() {
- return Collections.unmodifiableSortedSet(new TreeSet<String>(
- ldapTemplate.listBindings(groupBase, new ContextMapper() {
- public Object mapFromContext(Object ctxArg) {
- String groupName = ((DirContextAdapter) ctxArg)
- .getStringAttribute(groupRoleAttribute);
- String roleName = convertGroupToRole(groupName);
- return roleName;
- }
- })));
- }
-
- @SuppressWarnings("unchecked")
- public Set<String> listUsersInRole(String role) {
- return (Set<String>) ldapTemplate.lookup(
- buildGroupDn(convertRoleToGroup(role)), new ContextMapper() {
- public Object mapFromContext(Object ctxArg) {
- DirContextAdapter ctx = (DirContextAdapter) ctxArg;
- String[] userDns = ctx
- .getStringAttributes(groupMemberAttribute);
- TreeSet<String> set = new TreeSet<String>();
- for (String userDn : userDns) {
- DistinguishedName dn = new DistinguishedName(userDn);
- String username = dn.getValue(usernameAttribute);
- set.add(username);
- }
- return Collections.unmodifiableSortedSet(set);
- }
- });
- }
-
- public void createRole(String role, final String superuserName) {
- String group = convertRoleToGroup(role);
- DistinguishedName superuserDn = (DistinguishedName) ldapTemplate
- .executeReadWrite(new ContextExecutor() {
- public Object executeWithContext(DirContext ctx)
- throws NamingException {
- return LdapUtils.getFullDn(
- usernameMapper.buildDn(superuserName), ctx);
- }
- });
-
- Name groupDn = buildGroupDn(group);
- DirContextAdapter context = new DirContextAdapter();
- context.setAttributeValues("objectClass", groupClasses);
- context.setAttributeValue("cn", group);
- // Add superuser because cannot create empty group
- context.setAttributeValue(groupMemberAttribute, superuserDn.toString());
- ldapTemplate.bind(groupDn, context, null);
- }
-
- public void deleteRole(String role) {
- String group = convertRoleToGroup(role);
- Name dn = buildGroupDn(group);
- ldapTemplate.unbind(dn);
- }
-
- /** Maps a role (ROLE_XXX) to the related LDAP group (xxx) */
- protected String convertRoleToGroup(String role) {
- String group = role;
- if (group.startsWith(rolePrefix)) {
- group = group.substring(rolePrefix.length());
- group = group.toLowerCase();
- }
- return group;
- }
-
- /** Maps anLDAP group (xxx) to the related role (ROLE_XXX) */
- protected String convertGroupToRole(String groupName) {
- groupName = groupName.toUpperCase();
-
- return rolePrefix + groupName;
- }
-
- protected Name buildGroupDn(String name) {
- return new DistinguishedName(groupRoleAttribute + "=" + name + ","
- + groupBase);
- }
-
- public void setUserBase(String userBase) {
- this.userBase = userBase;
- }
-
- public void setUsernameAttribute(String usernameAttribute) {
- this.usernameAttribute = usernameAttribute;
- }
-
- public void setGroupBase(String groupBase) {
- this.groupBase = groupBase;
- }
-
- public void setGroupRoleAttribute(String groupRoleAttributeName) {
- this.groupRoleAttribute = groupRoleAttributeName;
- }
-
- public void setGroupMemberAttribute(String groupMemberAttributeName) {
- this.groupMemberAttribute = groupMemberAttributeName;
- }
-
- public void setDefaultRole(String defaultRole) {
- this.defaultRole = defaultRole;
- }
-
- public void setRolePrefix(String rolePrefix) {
- this.rolePrefix = rolePrefix;
- }
-
- public void setUsernameMapper(LdapUsernameToDnMapper usernameMapper) {
- this.usernameMapper = usernameMapper;
- }
-
- public String getDefaultRole() {
- return defaultRole;
- }
-
- public void setGroupClasses(String[] groupClasses) {
- this.groupClasses = groupClasses;
- }
-}
+++ /dev/null
-/*
- * Copyright (C) 2007-2012 Argeo GmbH
- *
- * 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 org.argeo.security.ldap.jcr;
-
-import java.security.NoSuchAlgorithmException;
-import java.security.SecureRandom;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Random;
-import java.util.SortedSet;
-
-import javax.jcr.Node;
-import javax.jcr.NodeIterator;
-import javax.jcr.Repository;
-import javax.jcr.RepositoryException;
-import javax.jcr.Session;
-import javax.jcr.query.Query;
-import javax.jcr.version.VersionManager;
-import javax.naming.Name;
-import javax.naming.directory.BasicAttribute;
-import javax.naming.directory.DirContext;
-import javax.naming.directory.ModificationItem;
-
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-import org.argeo.ArgeoException;
-import org.argeo.jcr.ArgeoNames;
-import org.argeo.jcr.ArgeoTypes;
-import org.argeo.jcr.JcrUtils;
-import org.argeo.security.SecurityUtils;
-import org.argeo.security.jcr.JcrSecurityModel;
-import org.argeo.security.jcr.JcrUserDetails;
-import org.argeo.security.jcr.SimpleJcrSecurityModel;
-import org.springframework.ldap.core.ContextMapper;
-import org.springframework.ldap.core.DirContextAdapter;
-import org.springframework.ldap.core.DirContextOperations;
-import org.springframework.ldap.core.DistinguishedName;
-import org.springframework.ldap.core.LdapTemplate;
-import org.springframework.security.authentication.encoding.PasswordEncoder;
-import org.springframework.security.core.GrantedAuthority;
-import org.springframework.security.core.userdetails.UserDetails;
-import org.springframework.security.ldap.LdapUsernameToDnMapper;
-import org.springframework.security.ldap.userdetails.UserDetailsContextMapper;
-
-/** Makes sure that LDAP and JCR are in line. */
-public class JcrLdapSynchronizer implements UserDetailsContextMapper,
- ArgeoNames {
- private final static Log log = LogFactory.getLog(JcrLdapSynchronizer.class);
-
- // LDAP
- private LdapTemplate ldapTemplate;
- /**
- * LDAP template whose context source has an object factory set to null. see
- * <a href=
- * "http://forum.springsource.org/showthread.php?55955-Persistent-search-with-spring-ldap"
- * >this</a>
- */
- // private LdapTemplate rawLdapTemplate;
-
- private String userBase;
- private String usernameAttribute;
- private String passwordAttribute;
- private String[] userClasses;
- // private String defaultUserRole ="ROLE_USER";
-
- // private NamingListener ldapUserListener;
- // private SearchControls subTreeSearchControls;
- private LdapUsernameToDnMapper usernameMapper;
-
- private PasswordEncoder passwordEncoder;
- private final Random random;
-
- // JCR
- /** Admin session on the main workspace */
- private Session nodeSession;
- private Repository repository;
-
- // private JcrProfileListener jcrProfileListener;
- private JcrSecurityModel jcrSecurityModel = new SimpleJcrSecurityModel();
-
- // Mapping
- private Map<String, String> propertyToAttributes = new HashMap<String, String>();
-
- public JcrLdapSynchronizer() {
- random = createRandom();
- }
-
- public void init() {
- try {
- nodeSession = repository.login();
-
- // TODO put this in a different thread, and poll the LDAP server
- // until it is up
- try {
- synchronize();
-
- // LDAP
- // subTreeSearchControls = new SearchControls();
- // subTreeSearchControls
- // .setSearchScope(SearchControls.SUBTREE_SCOPE);
- // LDAP listener
- // ldapUserListener = new LdapUserListener();
- // rawLdapTemplate.executeReadOnly(new ContextExecutor() {
- // public Object executeWithContext(DirContext ctx)
- // throws NamingException {
- // EventDirContext ectx = (EventDirContext) ctx.lookup("");
- // ectx.addNamingListener(userBase, "("
- // + usernameAttribute + "=*)",
- // subTreeSearchControls, ldapUserListener);
- // return null;
- // }
- // });
- } catch (Exception e) {
- log.error("Could not synchronize and listen to LDAP,"
- + " probably because the LDAP server is not available."
- + " Restart the system as soon as possible.", e);
- }
-
- // JCR
- // String[] nodeTypes = { ArgeoTypes.ARGEO_USER_PROFILE };
- // jcrProfileListener = new JcrProfileListener();
- // noLocal is used so that we are not notified when we modify JCR
- // from LDAP
- // nodeSession
- // .getWorkspace()
- // .getObservationManager()
- // .addEventListener(jcrProfileListener,
- // Event.PROPERTY_CHANGED | Event.NODE_ADDED, "/",
- // true, null, nodeTypes, true);
- } catch (Exception e) {
- JcrUtils.logoutQuietly(nodeSession);
- throw new ArgeoException("Cannot initialize LDAP/JCR synchronizer",
- e);
- }
- }
-
- public void destroy() {
- // JcrUtils.removeListenerQuietly(nodeSession, jcrProfileListener);
- JcrUtils.logoutQuietly(nodeSession);
- // try {
- // rawLdapTemplate.executeReadOnly(new ContextExecutor() {
- // public Object executeWithContext(DirContext ctx)
- // throws NamingException {
- // EventDirContext ectx = (EventDirContext) ctx.lookup("");
- // ectx.removeNamingListener(ldapUserListener);
- // return null;
- // }
- // });
- // } catch (Exception e) {
- // // silent (LDAP server may have been shutdown already)
- // if (log.isTraceEnabled())
- // log.trace("Cannot remove LDAP listener", e);
- // }
- }
-
- /*
- * LDAP TO JCR
- */
- /** Full synchronization between LDAP and JCR. LDAP has priority. */
- protected void synchronize() {
- try {
- Name userBaseName = new DistinguishedName(userBase);
- // TODO subtree search?
- @SuppressWarnings("unchecked")
- List<String> userPaths = (List<String>) ldapTemplate.listBindings(
- userBaseName, new ContextMapper() {
- public Object mapFromContext(Object ctxObj) {
- try {
- return mapLdapToJcr((DirContextAdapter) ctxObj);
- } catch (Exception e) {
- // do not break process because of error
- log.error(
- "Could not LDAP->JCR synchronize user "
- + ctxObj, e);
- return null;
- }
- }
- });
-
- // create accounts which are not in LDAP
- Query query = nodeSession
- .getWorkspace()
- .getQueryManager()
- .createQuery(
- "select * from [" + ArgeoTypes.ARGEO_USER_PROFILE
- + "]", Query.JCR_SQL2);
- NodeIterator it = query.execute().getNodes();
- while (it.hasNext()) {
- Node userProfile = it.nextNode();
- String path = userProfile.getPath();
- try {
- if (!userPaths.contains(path)) {
- String username = userProfile
- .getProperty(ARGEO_USER_ID).getString();
- // GrantedAuthority[] authorities = {new
- // GrantedAuthorityImpl(defaultUserRole)};
- List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();
- JcrUserDetails userDetails = new JcrUserDetails(
- userProfile, username, authorities);
- String dn = createLdapUser(userDetails);
- log.warn("Created ldap entry '" + dn + "' for user '"
- + username + "'");
-
- // if(!userProfile.getProperty(ARGEO_ENABLED).getBoolean()){
- // continue profiles;
- // }
- //
- // log.warn("Path "
- // + path
- // + " not found in LDAP, disabling user "
- // + userProfile.getProperty(ArgeoNames.ARGEO_USER_ID)
- // .getString());
-
- // Temporary hack to repair previous behaviour
- if (!userProfile.getProperty(ARGEO_ENABLED)
- .getBoolean()) {
- VersionManager versionManager = nodeSession
- .getWorkspace().getVersionManager();
- versionManager.checkout(userProfile.getPath());
- userProfile.setProperty(ArgeoNames.ARGEO_ENABLED,
- true);
- nodeSession.save();
- versionManager.checkin(userProfile.getPath());
- }
- }
- } catch (Exception e) {
- log.error("Cannot process " + path, e);
- }
- }
- } catch (Exception e) {
- JcrUtils.discardQuietly(nodeSession);
- log.error("Cannot synchronize LDAP and JCR", e);
- // throw new ArgeoException("Cannot synchronize LDAP and JCR", e);
- }
- }
-
- private String createLdapUser(UserDetails user) {
- DirContextAdapter ctx = new DirContextAdapter();
- mapUserToContext(user, ctx);
- DistinguishedName dn = usernameMapper.buildDn(user.getUsername());
- ldapTemplate.bind(dn, ctx, null);
- return dn.toString();
- }
-
- /** Called during authentication in order to retrieve user details */
- public UserDetails mapUserFromContext(final DirContextOperations ctx,
- final String username,
- Collection<? extends GrantedAuthority> authorities) {
- if (ctx == null)
- throw new ArgeoException("No LDAP information for user " + username);
-
- String ldapUsername = ctx.getStringAttribute(usernameAttribute);
- if (!ldapUsername.equals(username))
- throw new ArgeoException("Logged in with username " + username
- + " but LDAP user is " + ldapUsername);
-
- Node userProfile = jcrSecurityModel.sync(nodeSession, username,
- SecurityUtils.authoritiesToStringList(authorities));
- // JcrUserDetails.checkAccountStatus(userProfile);
-
- // password
- SortedSet<?> passwordAttributes = ctx
- .getAttributeSortedStringSet(passwordAttribute);
- String password;
- if (passwordAttributes == null || passwordAttributes.size() == 0) {
- // throw new ArgeoException("No password found for user " +
- // username);
- password = "NULL";
- } else {
- byte[] arr = (byte[]) passwordAttributes.first();
- password = new String(arr);
- // erase password
- Arrays.fill(arr, (byte) 0);
- }
-
- try {
- return new JcrUserDetails(userProfile, password, authorities);
- } catch (RepositoryException e) {
- throw new ArgeoException("Cannot retrieve user details for "
- + username, e);
- }
- }
-
- /**
- * Writes an LDAP context to the JCR user profile.
- *
- * @return path to user profile
- */
- protected synchronized String mapLdapToJcr(DirContextAdapter ctx) {
- Session session = nodeSession;
- try {
- // process
- String username = ctx.getStringAttribute(usernameAttribute);
-
- Node userProfile = jcrSecurityModel.sync(session, username, null);
- Map<String, String> modifications = new HashMap<String, String>();
- for (String jcrProperty : propertyToAttributes.keySet())
- ldapToJcr(userProfile, jcrProperty, ctx, modifications);
-
- int modifCount = modifications.size();
- if (modifCount > 0) {
- session.getWorkspace().getVersionManager()
- .checkout(userProfile.getPath());
- for (String prop : modifications.keySet())
- userProfile.setProperty(prop, modifications.get(prop));
- JcrUtils.updateLastModified(userProfile);
- session.save();
- session.getWorkspace().getVersionManager()
- .checkin(userProfile.getPath());
- if (log.isDebugEnabled())
- log.debug("Mapped " + modifCount + " LDAP modification"
- + (modifCount == 1 ? "" : "s") + " from "
- + ctx.getDn() + " to " + userProfile);
- }
- return userProfile.getPath();
- } catch (Exception e) {
- JcrUtils.discardQuietly(session);
- throw new ArgeoException("Cannot synchronize JCR and LDAP", e);
- }
- }
-
- /** Maps an LDAP property to a JCR property */
- protected void ldapToJcr(Node userProfile, String jcrProperty,
- DirContextOperations ctx, Map<String, String> modifications) {
- // TODO do we really need DirContextOperations?
- try {
- String ldapAttribute;
- if (propertyToAttributes.containsKey(jcrProperty))
- ldapAttribute = propertyToAttributes.get(jcrProperty);
- else
- throw new ArgeoException(
- "No LDAP attribute mapped for JCR proprty "
- + jcrProperty);
-
- String value = ctx.getStringAttribute(ldapAttribute);
- String jcrValue = userProfile.hasProperty(jcrProperty) ? userProfile
- .getProperty(jcrProperty).getString() : null;
- if (value != null && jcrValue != null) {
- if (!value.equals(jcrValue))
- modifications.put(jcrProperty, value);
- } else if (value != null && jcrValue == null) {
- modifications.put(jcrProperty, value);
- } else if (value == null && jcrValue != null) {
- modifications.put(jcrProperty, value);
- }
- } catch (Exception e) {
- throw new ArgeoException("Cannot map JCR property " + jcrProperty
- + " from LDAP", e);
- }
- }
-
- /*
- * JCR to LDAP
- */
-
- public void mapUserToContext(UserDetails user, final DirContextAdapter ctx) {
- if (!(user instanceof JcrUserDetails))
- throw new ArgeoException("Unsupported user details: "
- + user.getClass());
-
- ctx.setAttributeValues("objectClass", userClasses);
- ctx.setAttributeValue(usernameAttribute, user.getUsername());
- ctx.setAttributeValue(passwordAttribute,
- encodePassword(user.getPassword()));
-
- final JcrUserDetails jcrUserDetails = (JcrUserDetails) user;
- try {
- Node userProfile = nodeSession
- .getNode(jcrUserDetails.getHomePath()).getNode(
- ARGEO_PROFILE);
- for (String jcrProperty : propertyToAttributes.keySet()) {
- if (userProfile.hasProperty(jcrProperty)) {
- ModificationItem mi = jcrToLdap(jcrProperty, userProfile
- .getProperty(jcrProperty).getString());
- if (mi != null)
- ctx.setAttribute(mi.getAttribute());
- }
- }
- if (log.isTraceEnabled())
- log.trace("Mapped " + userProfile + " to " + ctx.getDn());
- } catch (RepositoryException e) {
- throw new ArgeoException("Cannot synchronize JCR and LDAP", e);
- }
-
- }
-
- /** Maps a JCR property to an LDAP property */
- protected ModificationItem jcrToLdap(String jcrProperty, String value) {
- // TODO do we really need DirContextOperations?
- try {
- String ldapAttribute;
- if (propertyToAttributes.containsKey(jcrProperty))
- ldapAttribute = propertyToAttributes.get(jcrProperty);
- else
- return null;
-
- // fix issue with empty 'sn' in LDAP
- if (ldapAttribute.equals("sn") && (value.trim().equals("")))
- return null;
- // fix issue with empty 'description' in LDAP
- if (ldapAttribute.equals("description") && value.trim().equals(""))
- return null;
- BasicAttribute attr = new BasicAttribute(
- propertyToAttributes.get(jcrProperty), value);
- ModificationItem mi = new ModificationItem(
- DirContext.REPLACE_ATTRIBUTE, attr);
- return mi;
- } catch (Exception e) {
- throw new ArgeoException("Cannot map JCR property " + jcrProperty
- + " from LDAP", e);
- }
- }
-
- /*
- * UTILITIES
- */
- protected String encodePassword(String password) {
- if (!password.startsWith("{")) {
- byte[] salt = new byte[16];
- random.nextBytes(salt);
- return passwordEncoder.encodePassword(password, salt);
- } else {
- return password;
- }
- }
-
- private static Random createRandom() {
- try {
- return SecureRandom.getInstance("SHA1PRNG");
- } catch (NoSuchAlgorithmException e) {
- return new Random(System.currentTimeMillis());
- }
- }
-
- /*
- * DEPENDENCY INJECTION
- */
-
- public void setLdapTemplate(LdapTemplate ldapTemplate) {
- this.ldapTemplate = ldapTemplate;
- }
-
- public void setRawLdapTemplate(LdapTemplate rawLdapTemplate) {
- // this.rawLdapTemplate = rawLdapTemplate;
- }
-
- public void setRepository(Repository repository) {
- this.repository = repository;
- }
-
- public void setUserBase(String userBase) {
- this.userBase = userBase;
- }
-
- public void setUsernameAttribute(String usernameAttribute) {
- this.usernameAttribute = usernameAttribute;
- }
-
- public void setPropertyToAttributes(Map<String, String> propertyToAttributes) {
- this.propertyToAttributes = propertyToAttributes;
- }
-
- public void setUsernameMapper(LdapUsernameToDnMapper usernameMapper) {
- this.usernameMapper = usernameMapper;
- }
-
- public void setPasswordAttribute(String passwordAttribute) {
- this.passwordAttribute = passwordAttribute;
- }
-
- public void setUserClasses(String[] userClasses) {
- this.userClasses = userClasses;
- }
-
- public void setPasswordEncoder(PasswordEncoder passwordEncoder) {
- this.passwordEncoder = passwordEncoder;
- }
-
- public void setJcrSecurityModel(JcrSecurityModel jcrSecurityModel) {
- this.jcrSecurityModel = jcrSecurityModel;
- }
-
- /** Listen to LDAP */
- // class LdapUserListener implements ObjectChangeListener,
- // NamespaceChangeListener, UnsolicitedNotificationListener {
- //
- // public void namingExceptionThrown(NamingExceptionEvent evt) {
- // evt.getException().printStackTrace();
- // }
- //
- // public void objectChanged(NamingEvent evt) {
- // Binding user = evt.getNewBinding();
- // // TODO find a way not to be called when JCR is the source of the
- // // modification
- // DirContextAdapter ctx = (DirContextAdapter) ldapTemplate
- // .lookup(user.getName());
- // mapLdapToJcr(ctx);
- // }
- //
- // public void objectAdded(NamingEvent evt) {
- // Binding user = evt.getNewBinding();
- // DirContextAdapter ctx = (DirContextAdapter) ldapTemplate
- // .lookup(user.getName());
- // mapLdapToJcr(ctx);
- // }
- //
- // public void objectRemoved(NamingEvent evt) {
- // if (log.isDebugEnabled())
- // log.debug(evt);
- // }
- //
- // public void objectRenamed(NamingEvent evt) {
- // if (log.isDebugEnabled())
- // log.debug(evt);
- // }
- //
- // public void notificationReceived(UnsolicitedNotificationEvent evt) {
- // UnsolicitedNotification notification = evt.getNotification();
- // NamingException ne = notification.getException();
- // String msg = "LDAP notification " + "ID=" + notification.getID()
- // + ", referrals=" + notification.getReferrals();
- // if (ne != null) {
- // if (log.isTraceEnabled())
- // log.trace(msg + ", exception= " + ne, ne);
- // else
- // log.warn(msg + ", exception= " + ne);
- // } else if (log.isDebugEnabled()) {
- // log.debug("Unsollicited LDAP notification " + msg);
- // }
- // }
- //
- // }
-
- /** Listen to JCR */
- // class JcrProfileListener implements EventListener {
- //
- // public void onEvent(EventIterator events) {
- // try {
- // final Map<Name, List<ModificationItem>> modifications = new HashMap<Name,
- // List<ModificationItem>>();
- // while (events.hasNext()) {
- // Event event = events.nextEvent();
- // try {
- // if (Event.PROPERTY_CHANGED == event.getType()) {
- // Property property = (Property) nodeSession
- // .getItem(event.getPath());
- // String propertyName = property.getName();
- // Node userProfile = property.getParent();
- // String username = userProfile.getProperty(
- // ARGEO_USER_ID).getString();
- // if (propertyToAttributes.containsKey(propertyName)) {
- // Name name = usernameMapper.buildDn(username);
- // if (!modifications.containsKey(name))
- // modifications.put(name,
- // new ArrayList<ModificationItem>());
- // String value = property.getString();
- // ModificationItem mi = jcrToLdap(propertyName,
- // value);
- // if (mi != null)
- // modifications.get(name).add(mi);
- // }
- // } else if (Event.NODE_ADDED == event.getType()) {
- // Node userProfile = nodeSession.getNode(event
- // .getPath());
- // String username = userProfile.getProperty(
- // ARGEO_USER_ID).getString();
- // Name name = usernameMapper.buildDn(username);
- // for (String propertyName : propertyToAttributes
- // .keySet()) {
- // if (!modifications.containsKey(name))
- // modifications.put(name,
- // new ArrayList<ModificationItem>());
- // String value = userProfile.getProperty(
- // propertyName).getString();
- // ModificationItem mi = jcrToLdap(propertyName,
- // value);
- // if (mi != null)
- // modifications.get(name).add(mi);
- // }
- // }
- // } catch (RepositoryException e) {
- // throw new ArgeoException("Cannot process event "
- // + event, e);
- // }
- // }
- //
- // for (Name name : modifications.keySet()) {
- // List<ModificationItem> userModifs = modifications.get(name);
- // int modifCount = userModifs.size();
- // ldapTemplate.modifyAttributes(name, userModifs
- // .toArray(new ModificationItem[modifCount]));
- // if (log.isDebugEnabled())
- // log.debug("Mapped " + modifCount + " JCR modification"
- // + (modifCount == 1 ? "" : "s") + " to " + name);
- // }
- // } catch (Exception e) {
- // // if (log.isDebugEnabled())
- // // e.printStackTrace();
- // throw new ArgeoException("Cannot process JCR events ("
- // + e.getMessage() + ")", e);
- // }
- // }
- //
- // }
-}
+++ /dev/null
-/*
- * Copyright (C) 2007-2012 Argeo GmbH
- *
- * 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 org.argeo.security.ldap.jcr;
-
-import java.util.Collection;
-import java.util.UUID;
-
-import javax.jcr.Node;
-import javax.jcr.Repository;
-import javax.jcr.RepositoryException;
-import javax.jcr.Session;
-
-import org.argeo.ArgeoException;
-import org.argeo.jcr.ArgeoNames;
-import org.argeo.jcr.JcrUtils;
-import org.argeo.jcr.UserJcrUtils;
-import org.argeo.security.jcr.JcrUserDetails;
-import org.springframework.ldap.core.DirContextAdapter;
-import org.springframework.ldap.core.DirContextOperations;
-import org.springframework.security.core.GrantedAuthority;
-import org.springframework.security.core.userdetails.UserDetails;
-import org.springframework.security.ldap.userdetails.UserDetailsContextMapper;
-
-/** @deprecated Read only mapping from LDAP to user details */
-@Deprecated
-public class JcrUserDetailsContextMapper implements UserDetailsContextMapper,
- ArgeoNames {
- /** Admin session on the security workspace */
- private Session securitySession;
- private Repository repository;
- private String securityWorkspace = "security";
-
- public void init() {
- try {
- securitySession = repository.login(securityWorkspace);
- } catch (RepositoryException e) {
- JcrUtils.logoutQuietly(securitySession);
- throw new ArgeoException(
- "Cannot initialize LDAP/JCR user details context mapper", e);
- }
- }
-
- public void destroy() {
- JcrUtils.logoutQuietly(securitySession);
- }
-
- /** Called during authentication in order to retrieve user details */
- public UserDetails mapUserFromContext(final DirContextOperations ctx,
- final String username,
- Collection<? extends GrantedAuthority> authorities) {
- if (ctx == null)
- throw new ArgeoException("No LDAP information for user " + username);
- Node userHome = UserJcrUtils.getUserHome(securitySession, username);
- if (userHome == null)
- throw new ArgeoException("No JCR information for user " + username);
-
- // password
- // SortedSet<?> passwordAttributes = ctx
- // .getAttributeSortedStringSet(passwordAttribute);
- // String password;
- // if (passwordAttributes == null || passwordAttributes.size() == 0) {
- // throw new ArgeoException("No password found for user " + username);
- // } else {
- // byte[] arr = (byte[]) passwordAttributes.first();
- // password = new String(arr);
- // // erase password
- // Arrays.fill(arr, (byte) 0);
- // }
-
- try {
- // we don't have access to password, so let's not pretend
- String password = UUID.randomUUID().toString();
- return new JcrUserDetails(userHome.getNode(ARGEO_PROFILE),
- password, authorities);
- } catch (RepositoryException e) {
- throw new ArgeoException("Cannot retrieve user details for "
- + username, e);
- }
- }
-
- public void mapUserToContext(UserDetails user, final DirContextAdapter ctx) {
- throw new UnsupportedOperationException("LDAP access is read-only");
- }
-
-}
<relativePath>..</relativePath>
</parent>
<artifactId>org.argeo.security.ui.admin</artifactId>
- <name>Commons Security Admin UI</name>
+ <name>Commons CMS Workbench Admin</name>
<packaging>jar</packaging>
<dependencies>
- <!-- Argeo Security -->
<dependency>
<groupId>org.argeo.commons</groupId>
- <artifactId>org.argeo.security.ui</artifactId>
+ <artifactId>org.argeo.util</artifactId>
<version>2.1.14-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.argeo.commons</groupId>
- <artifactId>org.argeo.security.core</artifactId>
+ <artifactId>org.argeo.security.ui</artifactId>
<version>2.1.14-SNAPSHOT</version>
</dependency>
-
- <!-- Argeo Eclipse, we build agaist RAP -->
<dependency>
<groupId>org.argeo.commons</groupId>
- <artifactId>org.argeo.eclipse.ui.rap</artifactId>
+ <artifactId>org.argeo.security.core</artifactId>
<version>2.1.14-SNAPSHOT</version>
- <scope>provided</scope>
</dependency>
- <!-- <dependency>
- <groupId>org.argeo.commons</groupId>
- <artifactId>org.argeo.eclipse.dep.rap</artifactId>
- <type>pom</type>
- <version>2.1.12-SNAPSHOT</version>
- <scope>provided</scope>
- </dependency> -->
<dependency>
<groupId>org.argeo.commons</groupId>
<artifactId>org.argeo.eclipse.ui</artifactId>
<version>2.1.14-SNAPSHOT</version>
</dependency>
-
- <!-- Commons -->
<dependency>
<groupId>org.argeo.commons</groupId>
- <artifactId>org.argeo.util</artifactId>
+ <artifactId>org.argeo.eclipse.ui.rap</artifactId>
<version>2.1.14-SNAPSHOT</version>
+ <scope>provided</scope>
</dependency>
-
- <!-- Spring -->
-<!-- <dependency> -->
-<!-- <groupId>org.argeo.tp</groupId> -->
-<!-- <artifactId>org.springframework.security.core</artifactId> -->
-<!-- </dependency> -->
-
- <!-- Others -->
-<!-- <dependency> -->
-<!-- <groupId>org.argeo.tp</groupId> -->
-<!-- <artifactId>slf4j.org.apache.commons.logging -->
-<!-- </artifactId> -->
-<!-- </dependency> -->
</dependencies>
</project>
\ No newline at end of file
<relativePath>..</relativePath>
</parent>
<artifactId>org.argeo.security.ui.rap</artifactId>
- <name>Commons Security UI RAP</name>
+ <name>Commons CMS Workbench RAP</name>
<packaging>jar</packaging>
<dependencies>
- <!-- Commons -->
<dependency>
<groupId>org.argeo.commons</groupId>
<artifactId>org.argeo.util</artifactId>
<version>2.1.14-SNAPSHOT</version>
</dependency>
-
- <!-- Logging -->
- <!-- <dependency> -->
- <!-- <groupId>org.argeo.tp</groupId> -->
- <!-- <artifactId>slf4j.org.apache.commons.logging</artifactId> -->
- <!-- </dependency> -->
-
- <!-- Argeo Security -->
<dependency>
<groupId>org.argeo.commons</groupId>
<artifactId>org.argeo.security.ui</artifactId>
<version>2.1.14-SNAPSHOT</version>
</dependency>
- <!-- <dependency> -->
- <!-- <groupId>org.argeo.commons</groupId> -->
- <!-- <artifactId>org.argeo.security.equinox</artifactId> -->
- <!-- <version>2.1.13-SNAPSHOT</version> -->
- <!-- </dependency> -->
-
- <!-- RAP specific -->
- <!-- <dependency> <groupId>org.argeo.commons</groupId> <artifactId>org.argeo.eclipse.dep.rap</artifactId>
- <type>pom</type> <version>2.1.12-SNAPSHOT</version> </dependency> -->
<dependency>
<groupId>org.argeo.commons</groupId>
<artifactId>org.argeo.eclipse.ui.rap</artifactId>
<relativePath>..</relativePath>
</parent>
<artifactId>org.argeo.security.ui</artifactId>
- <name>Commons Security UI</name>
+ <name>Commons CMS Workbench</name>
<packaging>jar</packaging>
<dependencies>
- <!-- Argeo Security -->
<dependency>
<groupId>org.argeo.commons</groupId>
<artifactId>org.argeo.security.core</artifactId>
<version>2.1.14-SNAPSHOT</version>
</dependency>
-
- <!-- Generic Eclipse Utilities -->
<dependency>
<groupId>org.argeo.commons</groupId>
<artifactId>org.argeo.eclipse.ui.workbench</artifactId>
<version>2.1.14-SNAPSHOT</version>
</dependency>
-
- <!-- We build against RAP -->
- <!-- <dependency>
- <groupId>org.argeo.commons</groupId>
- <artifactId>org.argeo.eclipse.dep.rap</artifactId>
- <type>pom</type>
- <version>2.1.12-SNAPSHOT</version>
- <scope>provided</scope>
- </dependency> -->
-
- <!-- Others: Commons utils, Spring, Logging -->
<dependency>
<groupId>org.argeo.commons</groupId>
<artifactId>org.argeo.util</artifactId>
<version>2.1.14-SNAPSHOT</version>
</dependency>
-<!-- <dependency> -->
-<!-- <groupId>org.argeo.tp</groupId> -->
-<!-- <artifactId>org.springframework.security.core</artifactId> -->
-<!-- </dependency> -->
-<!-- <dependency> -->
-<!-- <groupId>org.argeo.tp</groupId> -->
-<!-- <artifactId>slf4j.org.apache.commons.logging</artifactId> -->
-<!-- </dependency> -->
</dependencies>
</project>
\ No newline at end of file
+++ /dev/null
-<?xml version="1.0" encoding="UTF-8"?>
-<classpath>
- <classpathentry kind="src" path="src" />
- <classpathentry kind="con"
- path="org.eclipse.pde.core.requiredPlugins" />
- <classpathentry kind="con"
- path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6" />
- <classpathentry kind="output" path="bin" />
-</classpath>
+++ /dev/null
-<?xml version="1.0" encoding="UTF-8"?>
-<projectDescription>
- <name>org.argeo.server.core</name>
- <comment></comment>
- <projects>
- </projects>
- <buildSpec>
- <buildCommand>
- <name>org.eclipse.jdt.core.javabuilder</name>
- <arguments>
- </arguments>
- </buildCommand>
- <buildCommand>
- <name>org.eclipse.pde.ManifestBuilder</name>
- <arguments>
- </arguments>
- </buildCommand>
- <buildCommand>
- <name>org.eclipse.pde.SchemaBuilder</name>
- <arguments>
- </arguments>
- </buildCommand>
- </buildSpec>
- <natures>
- <nature>org.eclipse.jdt.core.javanature</nature>
- <nature>org.eclipse.pde.PluginNature</nature>
- </natures>
-</projectDescription>
+++ /dev/null
-Import-Package: javax.servlet,\
-org.springframework.web.context,\
-*
\ No newline at end of file
+++ /dev/null
-source.. = src/
-additional.bundles = org.apache.log4j,\
- com.jcraft.jsch
-
\ No newline at end of file
+++ /dev/null
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
- <modelVersion>4.0.0</modelVersion>
- <parent>
- <groupId>org.argeo.commons</groupId>
- <artifactId>argeo-commons</artifactId>
- <version>2.1.14-SNAPSHOT</version>
- <relativePath>..</relativePath>
- </parent>
- <artifactId>org.argeo.server.core</artifactId>
- <name>Commons Server Core</name>
- <dependencies>
- <dependency>
- <groupId>org.argeo.commons</groupId>
- <artifactId>org.argeo.util</artifactId>
- <version>2.1.14-SNAPSHOT</version>
- </dependency>
-
- <!-- Apache Commons -->
-<!-- <dependency>
- <groupId>org.argeo.tp</groupId>
- <artifactId>org.apache.commons.io</artifactId>
- </dependency>
- <dependency>
- <groupId>org.argeo.tp</groupId>
- <artifactId>org.apache.commons.vfs2</artifactId>
- </dependency>
- <dependency>
- <groupId>org.argeo.tp</groupId>
- <artifactId>org.apache.commons.exec</artifactId>
- </dependency>
- <dependency>
- <groupId>org.argeo.tp</groupId>
- <artifactId>com.jcraft.jsch</artifactId>
- </dependency>
-
- <dependency>
- <groupId>org.argeo.tp</groupId>
- <artifactId>org.joda.time</artifactId>
- </dependency>
- -->
- <!-- Spring -->
-<!-- <dependency>
- <groupId>org.argeo.tp</groupId>
- <artifactId>org.springframework.web</artifactId>
- </dependency>
- <dependency>
- <groupId>org.argeo.tp</groupId>
- <artifactId>org.springframework.beans</artifactId>
- </dependency>
- <dependency>
- <groupId>org.argeo.tp</groupId>
- <artifactId>org.springframework.context</artifactId>
- </dependency>
- <dependency>
- <groupId>org.argeo.tp</groupId>
- <artifactId>org.springframework.core</artifactId>
- </dependency>
- <dependency>
- <groupId>org.argeo.tp</groupId>
- <artifactId>org.springframework.web.servlet</artifactId>
- </dependency>
- -->
- <!-- J2EE -->
-<!-- <dependency> -->
-<!-- <groupId>org.argeo.tp</groupId> -->
-<!-- <artifactId>javax.servlet</artifactId> -->
-<!-- </dependency> -->
-
- <!-- Logging -->
-<!-- <dependency> -->
-<!-- <groupId>org.argeo.tp</groupId> -->
-<!-- <artifactId>slf4j.org.apache.commons.logging</artifactId> -->
-<!-- </dependency> -->
-
-<!-- <dependency> -->
-<!-- <groupId>org.argeo.tp</groupId> -->
-<!-- <artifactId>org.apache.commons.fileupload</artifactId> -->
-<!-- </dependency> -->
- </dependencies>
-</project>
\ No newline at end of file
+++ /dev/null
-/*
- * Copyright (C) 2007-2012 Argeo GmbH
- *
- * 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 org.argeo.server;
-
-
-/** Answer to an execution of a remote service which performed changes. */
-public class BooleanAnswer {
- private Boolean value = Boolean.TRUE;
-
- /** Canonical constructor */
- public BooleanAnswer(Boolean status) {
- this.value = status;
- }
-
- /** Empty constructor */
- public BooleanAnswer() {
- }
-
- public Boolean getValue() {
- return value;
- }
-
-}
+++ /dev/null
-/*
- * Copyright (C) 2007-2012 Argeo GmbH
- *
- * 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 org.argeo.server;
-
-import java.io.Reader;
-
-public interface Deserializer {
- public <T> T deserialize(Reader reader, Class<T> clss);
-}
+++ /dev/null
-/*
- * Copyright (C) 2007-2012 Argeo GmbH
- *
- * 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 org.argeo.server;
-
-import java.beans.PropertyEditorSupport;
-import java.io.StringReader;
-
-import org.apache.commons.io.IOUtils;
-
-public class DeserializingEditor extends PropertyEditorSupport {
- private final Deserializer deserializer;
- private final Class<?> targetClass;
-
- public DeserializingEditor(Deserializer deserializer, Class<?> targetClass) {
- super();
- this.deserializer = deserializer;
- this.targetClass = targetClass;
- }
-
- @Override
- public void setAsText(String text) throws IllegalArgumentException {
- StringReader reader = new StringReader(text);
- try {
- setValue(deserializer.deserialize(reader, targetClass));
- } finally {
- IOUtils.closeQuietly(reader);
- }
- }
-
-}
+++ /dev/null
-/*\r
- * Copyright (C) 2007-2012 Argeo GmbH\r
- *\r
- * Licensed under the Apache License, Version 2.0 (the "License");\r
- * you may not use this file except in compliance with the License.\r
- * You may obtain a copy of the License at\r
- *\r
- * http://www.apache.org/licenses/LICENSE-2.0\r
- *\r
- * Unless required by applicable law or agreed to in writing, software\r
- * distributed under the License is distributed on an "AS IS" BASIS,\r
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
- * See the License for the specific language governing permissions and\r
- * limitations under the License.\r
- */\r
-package org.argeo.server;\r
-\r
-/**\r
- * Answer to a request to a remote service that sends back only one primitive\r
- */\r
-public class PrimitiveAnswer {\r
-\r
- private Object primitive;\r
-\r
- /** Canonical constructor */\r
- public PrimitiveAnswer(Object primitive) {\r
- this.primitive = primitive;\r
- }\r
-\r
- public Object getValue() {\r
- return primitive;\r
- }\r
-\r
-}\r
+++ /dev/null
-/*
- * Copyright (C) 2007-2012 Argeo GmbH
- *
- * 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 org.argeo.server;
-
-import java.io.Writer;
-
-public interface Serializer {
- /** Will be removed soon. Use {@link #serialize(Object, Writer)} instead. */
- @Deprecated
- public void serialize(Writer writer, Object obj);
-
- public void serialize(Object obj, Writer writer);
-}
+++ /dev/null
-/*
- * Copyright (C) 2007-2012 Argeo GmbH
- *
- * 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 org.argeo.server;
-
-import java.io.PrintWriter;
-import java.io.StringWriter;
-
-import org.apache.commons.io.IOUtils;
-import org.argeo.ArgeoException;
-
-/** Answer to an execution of a remote service which performed changes. */
-public class ServerAnswer {
- public final static String OK = "OK";
- public final static String ERROR = "ERROR";
-
- private String status = OK;
- private String message = "";
-
- // TODO: add an exception field
-
- /** Canonical constructor */
- public ServerAnswer(String status, String message) {
- setStatus(status);
- if (message == null)
- throw new ArgeoException("Message cannot be null");
- this.message = message;
- }
-
- /** Empty constructor */
- public ServerAnswer() {
- }
-
- public String getStatus() {
- return status;
- }
-
- public void setStatus(String status) {
- if (status == null || (!status.equals(OK) && !status.equals(ERROR)))
- throw new ArgeoException("Bad status format: " + status);
- this.status = status;
- }
-
- public String getMessage() {
- return message;
- }
-
- public void setMessage(String message) {
- this.message = message;
- }
-
- public Boolean isOk() {
- return status.equals(OK);
- }
-
- public Boolean isError() {
- return status.equals(ERROR);
- }
-
- public static ServerAnswer error(String message) {
- return new ServerAnswer(ERROR, message);
- }
-
- public static ServerAnswer error(Throwable e) {
- StringWriter writer = new StringWriter();
- try {
- e.printStackTrace(new PrintWriter(writer));
- return new ServerAnswer(ERROR, writer.toString());
- } finally {
- IOUtils.closeQuietly(writer);
- }
- }
-
- public static ServerAnswer ok(String message) {
- return new ServerAnswer(OK, message);
- }
-
- @Override
- public String toString() {
- return "ServerAnswer{status:" + status + ", message:" + message + "}";
- }
-
-}
+++ /dev/null
-/*
- * Copyright (C) 2007-2012 Argeo GmbH
- *
- * 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 org.argeo.server;
-
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
-public interface ServerSerializer {
- public void serialize(Object obj, HttpServletRequest request,
- HttpServletResponse response);
-}
+++ /dev/null
-/*
- * Copyright (C) 2007-2012 Argeo GmbH
- *
- * 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 org.argeo.server.backup;
-
-import org.apache.commons.vfs2.FileObject;
-import org.apache.commons.vfs2.FileSystemManager;
-import org.apache.commons.vfs2.FileSystemOptions;
-import org.apache.commons.vfs2.provider.sftp.SftpFileSystemConfigBuilder;
-import org.argeo.ArgeoException;
-
-/**
- * Simplify atomic backups implementation, especially by managing VFS.
- */
-public abstract class AbstractAtomicBackup implements AtomicBackup {
- private String name;
- private String compression = "bz2";
-
- protected abstract void writeBackup(FileObject targetFo);
-
- public AbstractAtomicBackup() {
- }
-
- public AbstractAtomicBackup(String name) {
- this.name = name;
- }
-
- public void init() {
- if (name == null)
- throw new ArgeoException("Atomic backup name must be set");
- }
-
- public void destroy() {
-
- }
-
- @Override
- public String backup(FileSystemManager fileSystemManager,
- String backupsBase, BackupContext backupContext,
- FileSystemOptions opts) {
- if (name == null)
- throw new ArgeoException("Atomic backup name must be set");
-
- FileObject targetFo = null;
- try {
- if (backupsBase.startsWith("sftp:"))
- SftpFileSystemConfigBuilder.getInstance()
- .setStrictHostKeyChecking(opts, "no");
- if (compression == null || compression.equals("none"))
- targetFo = fileSystemManager.resolveFile(backupsBase + '/'
- + backupContext.getRelativeFolder() + '/' + name, opts);
- else if (compression.equals("bz2"))
- targetFo = fileSystemManager.resolveFile("bz2:" + backupsBase
- + '/' + backupContext.getRelativeFolder() + '/' + name
- + ".bz2" + "!" + name, opts);
- else if (compression.equals("gz"))
- targetFo = fileSystemManager.resolveFile("gz:" + backupsBase
- + '/' + backupContext.getRelativeFolder() + '/' + name
- + ".gz" + "!" + name, opts);
- else
- throw new ArgeoException("Unsupported compression "
- + compression);
-
- writeBackup(targetFo);
-
- return targetFo.toString();
- } catch (Exception e) {
- throw new ArgeoException("Cannot backup " + name + " to "
- + targetFo, e);
- } finally {
- BackupUtils.closeFOQuietly(targetFo);
- }
- }
-
- public void setName(String name) {
- this.name = name;
- }
-
- public String getName() {
- return name;
- }
-
- public void setCompression(String compression) {
- this.compression = compression;
- }
-}
+++ /dev/null
-/*
- * Copyright (C) 2007-2012 Argeo GmbH
- *
- * 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 org.argeo.server.backup;
-
-import org.apache.commons.vfs2.FileSystemManager;
-import org.apache.commons.vfs2.FileSystemOptions;
-
-/** Performs the backup of a single component, typically a database dump */
-public interface AtomicBackup {
- /** Name identifiying this backup */
- public String getName();
-
- /**
- * Retrieves the data of the component in a format that allows to restore
- * the component
- *
- * @param backupContext
- * the context of this backup
- * @return the VFS URI of the generated file or directory
- */
- public String backup(FileSystemManager fileSystemManager,
- String backupsBase, BackupContext backupContext,
- FileSystemOptions opts);
-}
+++ /dev/null
-/*
- * Copyright (C) 2007-2012 Argeo GmbH
- *
- * 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 org.argeo.server.backup;
-
-import java.text.DateFormat;
-import java.util.Date;
-
-/**
- * Transient information of a given backup, centralizing common information such
- * as timestamp and location.
- */
-public interface BackupContext {
- /** Backup date */
- public Date getTimestamp();
-
- /** Formatted backup date */
- public String getTimestampAsString();
-
- /** System name */
- public String getSystemName();
-
- /** Local base */
- public String getRelativeFolder();
-
- /** Date format */
- public DateFormat getDateFormat();
-}
+++ /dev/null
-/*
- * Copyright (C) 2007-2012 Argeo GmbH
- *
- * 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 org.argeo.server.backup;
-
-import org.apache.commons.vfs2.FileSystemException;
-import org.apache.commons.vfs2.impl.DefaultFileSystemManager;
-import org.apache.commons.vfs2.provider.bzip2.Bzip2FileProvider;
-import org.apache.commons.vfs2.provider.ftp.FtpFileProvider;
-import org.apache.commons.vfs2.provider.gzip.GzipFileProvider;
-import org.apache.commons.vfs2.provider.local.DefaultLocalFileProvider;
-import org.apache.commons.vfs2.provider.ram.RamFileProvider;
-import org.apache.commons.vfs2.provider.sftp.SftpFileProvider;
-import org.apache.commons.vfs2.provider.url.UrlFileProvider;
-import org.argeo.ArgeoException;
-
-/**
- * Programatically configured VFS file system manager which can be declared as a
- * bean and associated with a life cycle (methods
- * {@link DefaultFileSystemManager#init()} and
- * {@link DefaultFileSystemManager#closet()}). Supports bz2, file, ram, gzip,
- * ftp, sftp
- */
-public class BackupFileSystemManager extends DefaultFileSystemManager {
-
- public BackupFileSystemManager() {
- super();
- try {
- addProvider("file", new DefaultLocalFileProvider());
- addProvider("bz2", new Bzip2FileProvider());
- addProvider("ftp", new FtpFileProvider());
- addProvider("sftp", new SftpFileProvider());
- addProvider("gzip", new GzipFileProvider());
- addProvider("ram", new RamFileProvider());
- setDefaultProvider(new UrlFileProvider());
- } catch (FileSystemException e) {
- throw new ArgeoException("Cannot configure backup file provider", e);
- }
- }
-}
+++ /dev/null
-/*
- * Copyright (C) 2007-2012 Argeo GmbH
- *
- * 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 org.argeo.server.backup;
-
-import java.text.DateFormat;
-
-import org.apache.commons.vfs2.FileSystemManager;
-import org.apache.commons.vfs2.FileSystemOptions;
-
-/** Purges previous backups */
-public interface BackupPurge {
- /**
- * Purge the backups identified by these arguments. Although these are the
- * same fields as a {@link BackupContext} we don't pass it as argument since
- * we want to use this interface to purge remote backups as well (that is,
- * with a different base), or outside the scope of a running backup.
- */
- public void purge(FileSystemManager fileSystemManager, String base,
- String name, DateFormat dateFormat, FileSystemOptions opts);
-}
+++ /dev/null
-/*
- * Copyright (C) 2007-2012 Argeo GmbH
- *
- * 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 org.argeo.server.backup;
-
-import org.apache.commons.vfs2.FileObject;
-
-/** Backup utilities */
-public class BackupUtils {
- /** Close a file object quietly even if it is null or throws an exception. */
- public static void closeFOQuietly(FileObject fo) {
- if (fo != null) {
- try {
- fo.close();
- } catch (Exception e) {
- // silent
- }
- }
- }
-
- /** Prevents instantiation */
- private BackupUtils() {
- }
-}
+++ /dev/null
-/*
- * Copyright (C) 2007-2012 Argeo GmbH
- *
- * 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 org.argeo.server.backup;
-
-import org.apache.commons.vfs2.FileObject;
-
-/** Backups a MySQL database using mysqldump. */
-public class MySqlBackup extends OsCallBackup {
- private String mysqldumpLocation = "/usr/bin/mysqldump";
-
- private String dbUser;
- private String dbPassword;
- private String dbName;
-
- public MySqlBackup() {
- }
-
- public MySqlBackup(String dbUser, String dbPassword, String dbName) {
- this.dbUser = dbUser;
- this.dbPassword = dbPassword;
- this.dbName = dbName;
- init();
- }
-
- @Override
- public void init() {
- if (getName() == null)
- setName(dbName + ".mysql");
- super.init();
- }
-
- @Override
- public void writeBackup(FileObject targetFo) {
- if (getCommand() == null)
- setCommand(mysqldumpLocation
- + " --lock-tables --add-locks --add-drop-table"
- + " -u ${dbUser} --password=${dbPassword} --databases ${dbName}");
- getVariables().put("dbUser", dbUser);
- getVariables().put("dbPassword", dbPassword);
- getVariables().put("dbName", dbName);
-
- super.writeBackup(targetFo);
- }
-
- public void setDbUser(String dbUser) {
- this.dbUser = dbUser;
- }
-
- public void setDbPassword(String dbPassword) {
- this.dbPassword = dbPassword;
- }
-
- public void setDbName(String dbName) {
- this.dbName = dbName;
- }
-
- public void setMysqldumpLocation(String mysqldumpLocation) {
- this.mysqldumpLocation = mysqldumpLocation;
- }
-
-}
+++ /dev/null
-/*
- * Copyright (C) 2007-2012 Argeo GmbH
- *
- * 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 org.argeo.server.backup;
-
-import org.apache.commons.vfs2.FileObject;
-import org.argeo.ArgeoException;
-
-/** Backups an OpenLDAP server using slapcat */
-public class OpenLdapBackup extends OsCallBackup {
- private String slapcatLocation = "/usr/sbin/slapcat";
- private String slapdConfLocation = "/etc/openldap/slapd.conf";
- private String baseDn;
-
- public OpenLdapBackup() {
- super();
- }
-
- public OpenLdapBackup(String baseDn) {
- super();
- this.baseDn = baseDn;
- }
-
- @Override
- public void writeBackup(FileObject targetFo) {
- if (baseDn == null)
- throw new ArgeoException("Base DN must be set");
-
- if (getCommand() == null)
- setCommand(slapcatLocation
- + " -f ${slapdConfLocation} -b '${baseDn}'");
- getVariables().put("slapdConfLocation", slapdConfLocation);
- getVariables().put("baseDn", baseDn);
-
- super.writeBackup(targetFo);
- }
-
- public void setSlapcatLocation(String slapcatLocation) {
- this.slapcatLocation = slapcatLocation;
- }
-
- public void setSlapdConfLocation(String slapdConfLocation) {
- this.slapdConfLocation = slapdConfLocation;
- }
-
- public void setBaseDn(String baseDn) {
- this.baseDn = baseDn;
- }
-
-}
+++ /dev/null
-/*
- * Copyright (C) 2007-2012 Argeo GmbH
- *
- * 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 org.argeo.server.backup;
-
-import java.io.ByteArrayOutputStream;
-import java.util.HashMap;
-import java.util.Map;
-
-import org.apache.commons.exec.CommandLine;
-import org.apache.commons.exec.DefaultExecutor;
-import org.apache.commons.exec.ExecuteException;
-import org.apache.commons.exec.ExecuteStreamHandler;
-import org.apache.commons.exec.Executor;
-import org.apache.commons.exec.PumpStreamHandler;
-import org.apache.commons.io.IOUtils;
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-import org.apache.commons.vfs2.FileContent;
-import org.apache.commons.vfs2.FileObject;
-import org.argeo.ArgeoException;
-
-/**
- * Runs an OS command and save its standard output as a file. Typically used for
- * MySQL or OpenLDAP dumps.
- */
-public class OsCallBackup extends AbstractAtomicBackup {
- private final static Log log = LogFactory.getLog(OsCallBackup.class);
-
- private String command;
- private Map<String, String> variables = new HashMap<String, String>();
- private Executor executor = new DefaultExecutor();
-
- private Map<String, String> environment = new HashMap<String, String>();
-
- /** Name of the sudo user, root if "", not sudo if null */
- private String sudo = null;
-
- public OsCallBackup() {
- }
-
- public OsCallBackup(String name) {
- super(name);
- }
-
- public OsCallBackup(String name, String command) {
- super(name);
- this.command = command;
- }
-
- @Override
- public void writeBackup(FileObject targetFo) {
- String commandToUse = command;
-
- // sudo
- if (sudo != null) {
- if (sudo.equals(""))
- commandToUse = "sudo " + commandToUse;
- else
- commandToUse = "sudo -u " + sudo + " " + commandToUse;
- }
-
- CommandLine commandLine = CommandLine.parse(commandToUse, variables);
- ByteArrayOutputStream errBos = new ByteArrayOutputStream();
- if (log.isTraceEnabled())
- log.trace(commandLine.toString());
-
- try {
- // stdout
- FileContent targetContent = targetFo.getContent();
- // stderr
- ExecuteStreamHandler streamHandler = new PumpStreamHandler(
- targetContent.getOutputStream(), errBos);
- executor.setStreamHandler(streamHandler);
- executor.execute(commandLine, environment);
- } catch (ExecuteException e) {
- byte[] err = errBos.toByteArray();
- String errStr = new String(err);
- throw new ArgeoException("Process " + commandLine + " failed ("
- + e.getExitValue() + "): " + errStr, e);
- } catch (Exception e) {
- byte[] err = errBos.toByteArray();
- String errStr = new String(err);
- throw new ArgeoException("Process " + commandLine + " failed: "
- + errStr, e);
- } finally {
- IOUtils.closeQuietly(errBos);
- }
- }
-
- public void setCommand(String command) {
- this.command = command;
- }
-
- protected String getCommand() {
- return command;
- }
-
- /**
- * A reference to the environment variables that will be passed to the
- * process. Empty by default.
- */
- protected Map<String, String> getEnvironment() {
- return environment;
- }
-
- protected Map<String, String> getVariables() {
- return variables;
- }
-
- public void setVariables(Map<String, String> variables) {
- this.variables = variables;
- }
-
- public void setExecutor(Executor executor) {
- this.executor = executor;
- }
-
- public void setSudo(String sudo) {
- this.sudo = sudo;
- }
-
-}
+++ /dev/null
-/*
- * Copyright (C) 2007-2012 Argeo GmbH
- *
- * 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 org.argeo.server.backup;
-
-import org.apache.commons.vfs2.FileObject;
-
-/** Backups a PostgreSQL database using pg_dump. */
-public class PostgreSqlBackup extends OsCallBackup {
- /**
- * PostgreSQL password environment variable (see
- * http://stackoverflow.com/questions
- * /2893954/how-to-pass-in-password-to-pg-dump)
- */
- protected final static String PGPASSWORD = "PGPASSWORD";
-
- private String pgDumpLocation = "/usr/bin/pg_dump";
-
- private String dbUser;
- private String dbPassword;
- private String dbName;
-
- public PostgreSqlBackup() {
- super();
- }
-
- public PostgreSqlBackup(String dbUser, String dbPassword, String dbName) {
- this.dbUser = dbUser;
- this.dbPassword = dbPassword;
- this.dbName = dbName;
- init();
- }
-
- @Override
- public void init() {
- // disable compression since pg_dump is used with -Fc option
- setCompression(null);
-
- if (getName() == null)
- setName(dbName + ".pgdump");
- super.init();
- }
-
- @Override
- public void writeBackup(FileObject targetFo) {
- if (getCommand() == null) {
- getEnvironment().put(PGPASSWORD, dbPassword);
- setCommand(pgDumpLocation + " -Fc" + " -U ${dbUser} ${dbName}");
- }
- getVariables().put("dbUser", dbUser);
- getVariables().put("dbPassword", dbPassword);
- getVariables().put("dbName", dbName);
-
- super.writeBackup(targetFo);
- }
-
- public void setDbUser(String dbUser) {
- this.dbUser = dbUser;
- }
-
- public void setDbPassword(String dbPassword) {
- this.dbPassword = dbPassword;
- }
-
- public void setDbName(String dbName) {
- this.dbName = dbName;
- }
-
- public void setPgDumpLocation(String mysqldumpLocation) {
- this.pgDumpLocation = mysqldumpLocation;
- }
-
-}
+++ /dev/null
-/*
- * Copyright (C) 2007-2012 Argeo GmbH
- *
- * 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 org.argeo.server.backup;
-
-import java.text.DateFormat;
-import java.text.SimpleDateFormat;
-import java.util.Date;
-
-import org.apache.commons.vfs2.FileSystemManager;
-
-/** Simple implementation of a backup context */
-public class SimpleBackupContext implements BackupContext {
- private DateFormat dateFormat = new SimpleDateFormat("yyyyMMdd_HHmm");
- private final Date timestamp;
- private final String name;
-
- private final FileSystemManager fileSystemManager;
-
- public SimpleBackupContext(FileSystemManager fileSystemManager,
- String backupsBase, String name) {
- this.name = name;
- this.timestamp = new Date();
- this.fileSystemManager = fileSystemManager;
- }
-
- public Date getTimestamp() {
- return timestamp;
- }
-
- public String getTimestampAsString() {
- return dateFormat.format(timestamp);
- }
-
- public String getSystemName() {
- return name;
- }
-
- public String getRelativeFolder() {
- return name + '/' + getTimestampAsString();
- }
-
- public DateFormat getDateFormat() {
- return dateFormat;
- }
-
- public FileSystemManager getFileSystemManager() {
- return fileSystemManager;
- }
-
-}
+++ /dev/null
-/*
- * Copyright (C) 2007-2012 Argeo GmbH
- *
- * 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 org.argeo.server.backup;
-
-import java.text.DateFormat;
-import java.util.Date;
-import java.util.SortedMap;
-import java.util.TreeMap;
-
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-import org.apache.commons.vfs2.FileObject;
-import org.apache.commons.vfs2.FileSystemManager;
-import org.apache.commons.vfs2.FileSystemOptions;
-import org.apache.commons.vfs2.Selectors;
-import org.argeo.ArgeoException;
-import org.joda.time.DateTime;
-import org.joda.time.Period;
-
-/** Simple backup purge which keeps backups only for a given number of days */
-public class SimpleBackupPurge implements BackupPurge {
- private final static Log log = LogFactory.getLog(SimpleBackupPurge.class);
-
- private Integer daysKept = 30;
-
- @Override
- public void purge(FileSystemManager fileSystemManager, String base,
- String name, DateFormat dateFormat, FileSystemOptions opts) {
- try {
- DateTime nowDt = new DateTime();
- FileObject baseFo = fileSystemManager.resolveFile(
- base + '/' + name, opts);
-
- SortedMap<DateTime, FileObject> toDelete = new TreeMap<DateTime, FileObject>();
- int backupCount = 0;
-
- // make sure base dir exists
- baseFo.createFolder();
-
- // scan backups and list those which should be deleted
- for (FileObject backupFo : baseFo.getChildren()) {
- String backupName = backupFo.getName().getBaseName();
- Date backupDate = dateFormat.parse(backupName);
- backupCount++;
-
- DateTime backupDt = new DateTime(backupDate.getTime());
- Period sinceThen = new Period(backupDt, nowDt);
- int days = sinceThen.getDays();
- // int days = sinceThen.getMinutes();
- if (days > daysKept) {
- toDelete.put(backupDt, backupFo);
- }
- }
-
- if (toDelete.size() != 0 && toDelete.size() == backupCount) {
- // all backups would be deleted
- // but we want to keep at least one
- DateTime lastBackupDt = toDelete.firstKey();
- FileObject keptFo = toDelete.remove(lastBackupDt);
- log.warn("Backup " + keptFo
- + " kept although it is older than " + daysKept
- + " days.");
- }
-
- // delete old backups
- for (FileObject backupFo : toDelete.values()) {
- backupFo.delete(Selectors.SELECT_ALL);
- if (log.isDebugEnabled())
- log.debug("Deleted backup " + backupFo);
- }
- } catch (Exception e) {
- throw new ArgeoException("Could not purge previous backups", e);
- }
-
- }
-
-}
+++ /dev/null
-/*
- * Copyright (C) 2007-2012 Argeo GmbH
- *
- * 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 org.argeo.server.backup;
-
-import java.io.File;
-
-import org.apache.commons.vfs2.FileObject;
-
-/** Backups a Subversion repository using svnadmin. */
-public class SvnBackup extends OsCallBackup {
- private String svnadminLocation = "/usr/bin/svnadmin";
-
- private String repoLocation;
- private String repoName;
-
- public SvnBackup() {
- }
-
- public SvnBackup(String repoLocation) {
- this.repoLocation = repoLocation;
- init();
- }
-
- @Override
- public void init() {
- // use directory as repo name
- if (repoName == null)
- repoName = new File(repoLocation).getName();
-
- if (getName() == null)
- setName(repoName + ".svndump");
- super.init();
- }
-
- @Override
- public void writeBackup(FileObject targetFo) {
- if (getCommand() == null) {
- setCommand(svnadminLocation + " dump " + " ${repoLocation}");
- }
- getVariables().put("repoLocation", repoLocation);
-
- super.writeBackup(targetFo);
- }
-
- public void setRepoLocation(String repoLocation) {
- this.repoLocation = repoLocation;
- }
-
- public void setRepoName(String repoName) {
- this.repoName = repoName;
- }
-
- public void setSvnadminLocation(String mysqldumpLocation) {
- this.svnadminLocation = mysqldumpLocation;
- }
-
-}
+++ /dev/null
-/*
- * Copyright (C) 2007-2012 Argeo GmbH
- *
- * 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 org.argeo.server.backup;
-
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-import org.apache.commons.vfs2.FileObject;
-import org.apache.commons.vfs2.FileSystemException;
-import org.apache.commons.vfs2.FileSystemManager;
-import org.apache.commons.vfs2.FileSystemOptions;
-import org.apache.commons.vfs2.Selectors;
-import org.apache.commons.vfs2.UserAuthenticator;
-import org.apache.commons.vfs2.auth.StaticUserAuthenticator;
-import org.apache.commons.vfs2.impl.DefaultFileSystemConfigBuilder;
-import org.apache.commons.vfs2.impl.StandardFileSystemManager;
-import org.argeo.ArgeoException;
-
-/**
- * Combines multiple backups and transfer them to a remote location. Purges
- * remote and local data based on certain criteria.
- */
-public class SystemBackup implements Runnable {
- private final static Log log = LogFactory.getLog(SystemBackup.class);
-
- private FileSystemManager fileSystemManager;
- private UserAuthenticator userAuthenticator = null;
-
- private String backupsBase;
- private String systemName;
-
- private List<AtomicBackup> atomicBackups = new ArrayList<AtomicBackup>();
- private BackupPurge backupPurge = new SimpleBackupPurge();
-
- private Map<String, UserAuthenticator> remoteBases = new HashMap<String, UserAuthenticator>();
-
- @Override
- public void run() {
- if (atomicBackups.size() == 0)
- throw new ArgeoException("No atomic backup listed");
- List<String> failures = new ArrayList<String>();
-
- SimpleBackupContext backupContext = new SimpleBackupContext(
- fileSystemManager, backupsBase, systemName);
-
- // purge older backups
- FileSystemOptions opts = new FileSystemOptions();
- try {
- DefaultFileSystemConfigBuilder.getInstance().setUserAuthenticator(
- opts, userAuthenticator);
- } catch (FileSystemException e) {
- throw new ArgeoException("Cannot create authentication", e);
- }
-
- try {
-
- backupPurge.purge(fileSystemManager, backupsBase, systemName,
- backupContext.getDateFormat(), opts);
- } catch (Exception e) {
- failures.add("Purge " + backupsBase + " failed: " + e.getMessage());
- log.error("Purge of " + backupsBase + " failed", e);
- }
-
- // perform backup
- for (AtomicBackup atomickBackup : atomicBackups) {
- try {
- String target = atomickBackup.backup(fileSystemManager,
- backupsBase, backupContext, opts);
- if (log.isDebugEnabled())
- log.debug("Performed backup " + target);
- } catch (Exception e) {
- String msg = "Atomic backup " + atomickBackup.getName()
- + " failed: " + ArgeoException.chainCausesMessages(e);
- failures.add(msg);
- log.error(msg);
- if (log.isTraceEnabled())
- log.trace(
- "Stacktrace of atomic backup "
- + atomickBackup.getName() + " failure.", e);
- }
- }
-
- // dispatch to remote
- for (String remoteBase : remoteBases.keySet()) {
- FileObject localBaseFo = null;
- FileObject remoteBaseFo = null;
- UserAuthenticator auth = remoteBases.get(remoteBase);
-
- // authentication
- FileSystemOptions remoteOpts = new FileSystemOptions();
- try {
- DefaultFileSystemConfigBuilder.getInstance()
- .setUserAuthenticator(remoteOpts, auth);
- backupPurge.purge(fileSystemManager, remoteBase, systemName,
- backupContext.getDateFormat(), remoteOpts);
- } catch (Exception e) {
- failures.add("Purge " + remoteBase + " failed: "
- + e.getMessage());
- log.error("Cannot purge " + remoteBase, e);
- }
-
- try {
- localBaseFo = fileSystemManager.resolveFile(backupsBase + '/'
- + backupContext.getRelativeFolder(), opts);
- remoteBaseFo = fileSystemManager.resolveFile(remoteBase + '/'
- + backupContext.getRelativeFolder(), remoteOpts);
- remoteBaseFo.copyFrom(localBaseFo, Selectors.SELECT_ALL);
- if (log.isDebugEnabled())
- log.debug("Copied backup to " + remoteBaseFo + " from "
- + localBaseFo);
- // }
- } catch (Exception e) {
- failures.add("Dispatch to " + remoteBase + " failed: "
- + e.getMessage());
- log.error(
- "Cannot dispatch backups from "
- + backupContext.getRelativeFolder() + " to "
- + remoteBase, e);
- }
- BackupUtils.closeFOQuietly(localBaseFo);
- BackupUtils.closeFOQuietly(remoteBaseFo);
- }
-
- int failureCount = 0;
- if (failures.size() > 0) {
- StringBuffer buf = new StringBuffer();
- for (String failure : failures) {
- buf.append('\n').append(failureCount).append(" - ")
- .append(failure);
- failureCount++;
- }
- throw new ArgeoException(failureCount
- + " error(s) when running the backup,"
- + " check the logs and the backups as soon as possible."
- + buf);
- }
- }
-
- public void setFileSystemManager(FileSystemManager fileSystemManager) {
- this.fileSystemManager = fileSystemManager;
- }
-
- public void setBackupsBase(String backupsBase) {
- this.backupsBase = backupsBase;
- }
-
- public void setSystemName(String name) {
- this.systemName = name;
- }
-
- public void setAtomicBackups(List<AtomicBackup> atomicBackups) {
- this.atomicBackups = atomicBackups;
- }
-
- public void setBackupPurge(BackupPurge backupPurge) {
- this.backupPurge = backupPurge;
- }
-
- public void setUserAuthenticator(UserAuthenticator userAuthenticator) {
- this.userAuthenticator = userAuthenticator;
- }
-
- public void setRemoteBases(Map<String, UserAuthenticator> remoteBases) {
- this.remoteBases = remoteBases;
- }
-
- public static void main(String args[]) {
- while (true) {
- try {
- StandardFileSystemManager fsm = new StandardFileSystemManager();
- fsm.init();
-
- SystemBackup systemBackup = new SystemBackup();
- systemBackup.setSystemName("mySystem");
- systemBackup
- .setBackupsBase("/home/mbaudier/dev/src/commons/server/runtime/org.argeo.server.core/target");
- systemBackup.setFileSystemManager(fsm);
-
- List<AtomicBackup> atomicBackups = new ArrayList<AtomicBackup>();
-
- MySqlBackup mySqlBackup = new MySqlBackup("root", "", "test");
- atomicBackups.add(mySqlBackup);
- PostgreSqlBackup postgreSqlBackup = new PostgreSqlBackup(
- "argeo", "argeo", "gis_template");
- atomicBackups.add(postgreSqlBackup);
- SvnBackup svnBackup = new SvnBackup(
- "/home/mbaudier/tmp/testsvnrepo");
- atomicBackups.add(svnBackup);
-
- systemBackup.setAtomicBackups(atomicBackups);
-
- Map<String, UserAuthenticator> remoteBases = new HashMap<String, UserAuthenticator>();
- StaticUserAuthenticator userAuthenticator = new StaticUserAuthenticator(
- null, "demo", "demo");
- remoteBases.put("sftp://localhost/home/mbaudier/test",
- userAuthenticator);
- systemBackup.setRemoteBases(remoteBases);
-
- systemBackup.run();
-
- fsm.close();
- } catch (FileSystemException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- System.exit(1);
- }
-
- // wait
- try {
- Thread.sleep(120 * 1000);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- }
-}
+++ /dev/null
-/*
- * Copyright (C) 2007-2012 Argeo GmbH
- *
- * 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 org.argeo.server.dao;
-
-import java.beans.PropertyEditor;
-import java.io.InputStream;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.TreeMap;
-
-import org.apache.commons.io.IOUtils;
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-import org.argeo.ArgeoException;
-import org.springframework.beans.BeanWrapper;
-import org.springframework.beans.BeanWrapperImpl;
-import org.springframework.beans.BeansException;
-import org.springframework.beans.factory.InitializingBean;
-import org.springframework.context.ApplicationContext;
-import org.springframework.context.ApplicationContextAware;
-import org.springframework.core.io.Resource;
-
-public abstract class AbstractMemoryDaoSupport implements LightDaoSupport,
- ApplicationContextAware, InitializingBean {
- private final static Log log = LogFactory
- .getLog(AbstractMemoryDaoSupport.class);
-
- private ClassLoader classLoader = getClass().getClassLoader();
- private ApplicationContext applicationContext;
- private List<Class<?>> additionalClasses = new ArrayList<Class<?>>();
-
- private Map<Class<?>, Map<Object, Object>> model = new HashMap<Class<?>, Map<Object, Object>>();
-
- private Map<String, Object> externalRefs = new HashMap<String, Object>();
-
- private List<String> scannedPackages = new ArrayList<String>();
-
- private List<Resource> resources = new ArrayList<Resource>();
-
- private Map<Class<?>, PropertyEditor> customEditors = new HashMap<Class<?>, PropertyEditor>();;
-
- protected abstract void load(InputStream in, List<Reference> references);
-
- protected abstract Object findInternalRef(Reference reference);
-
- public void afterPropertiesSet() throws Exception {
- init();
- }
-
- public void init() {
- for (PropertyEditor propertyEditor : customEditors.values())
- if (propertyEditor instanceof LightDaoAware) {
- ((LightDaoAware) propertyEditor).setLightDaoSupport(this);
- }
-
- // Load data
- List<Reference> references = new ArrayList<Reference>();
-
- for (Resource res : resources) {
- InputStream in = null;
- try {
- in = res.getInputStream();
- load(in, references);
- } catch (Exception e) {
- throw new ArgeoException("Cannot load stream", e);
- } finally {
- IOUtils.closeQuietly(in);
- }
- }
-
- // Inject references
- for (Reference ref : references) {
- injectReference(ref);
- }
- if (log.isDebugEnabled())
- log.debug(references.size() + " references linked");
- }
-
- public List<Class<?>> getSupportedClasses() {
- List<Class<?>> res = new ArrayList<Class<?>>();
- res.addAll(additionalClasses);
- res.addAll(model.keySet());
- return res;
- }
-
- protected void injectReference(Reference reference) {
- BeanWrapper bw = new BeanWrapperImpl(reference.object);
- Object targetObject;
- if (reference.getExternalRef() != null) {
- String ref = reference.getExternalRef();
- if (externalRefs.containsKey(ref))
- targetObject = externalRefs.get(ref);
- else if (applicationContext != null)
- targetObject = applicationContext.getBean(ref);
- else {
- targetObject = null;
- log.warn("Ref " + ref + " not found");
- }
- } else {
- targetObject = findInternalRef(reference);
- }
- bw.setPropertyValue(reference.property, targetObject);
-
- }
-
- protected BeanWrapper newBeanWrapper(Class<?> targetClass) {
- BeanWrapperImpl bw = new BeanWrapperImpl(targetClass);
- for (Class<?> clss : customEditors.keySet())
- bw.registerCustomEditor(clss, customEditors.get(clss));
- return bw;
- }
-
- @SuppressWarnings("unchecked")
- public <T> T getByKey(Class<T> clss, Object key) {
- if (key == null)
- throw new ArgeoException("Key is null for " + clss);
- return (T) model.get(findClass(clss)).get(key);
- }
-
- /**
- * Slow.
- *
- * @return the first found
- */
- public <T> T getByField(Class<T> clss, String field, Object value) {
- List<T> all = list(clss, null);
- T res = null;
- for (T obj : all) {
- if (new BeanWrapperImpl(obj).getPropertyValue(field).equals(value)) {
- res = obj;
- break;
- }
- }
- return res;
- }
-
- @SuppressWarnings({ "unchecked", "rawtypes" })
- public <T> List<T> list(Class<T> clss, Object filter) {
- List<T> res = new ArrayList<T>();
-
- Class classToUse = findClass(clss);
- if (classToUse != null)
- res.addAll((Collection<T>) model.get(classToUse).values());
-
- if (applicationContext != null)
- res.addAll(applicationContext.getBeansOfType(clss).values());
-
- return res;
- }
-
- @SuppressWarnings({ "unchecked", "rawtypes" })
- protected Class findClass(Class parent) {
- if (model.containsKey(parent))
- return parent;
-
- for (Class clss : model.keySet()) {
- if (parent.isAssignableFrom(clss))
- return clss;// return the first found
- }
- if (log.isDebugEnabled())
- log.warn("No class found for " + parent.getName());
- return null;
- }
-
- public void setApplicationContext(ApplicationContext applicationContext)
- throws BeansException {
- this.applicationContext = applicationContext;
- }
-
- /**
- * When it should be stored under a different class (e.g. super class or
- * interface)
- */
- public void saveOrUpdate(Object key, Object value, Class<?> clss) {
- if (!model.containsKey(clss))
- model.put(clss, new TreeMap<Object, Object>());
- model.get(clss).put(key, value);
- }
-
- protected ClassLoader getClassLoader() {
- return classLoader;
- }
-
- public void setExternalRefs(Map<String, Object> externalRefs) {
- this.externalRefs = externalRefs;
- }
-
- public Map<String, Object> getExternalRefs() {
- return externalRefs;
- }
-
- public void setScannedPackages(List<String> scannedPackages) {
- this.scannedPackages = scannedPackages;
- }
-
- public List<String> getScannedPackages() {
- return scannedPackages;
- }
-
- public void setResources(List<Resource> workbooks) {
- this.resources = workbooks;
- }
-
- public List<Resource> getResources() {
- return resources;
- }
-
- public void setClassLoader(ClassLoader classLoader) {
- this.classLoader = classLoader;
- }
-
- public List<Class<?>> getAdditionalClasses() {
- return additionalClasses;
- }
-
- public void setAdditionalClasses(List<Class<?>> additionalClasses) {
- this.additionalClasses = additionalClasses;
- }
-
- public void setCustomEditors(Map<Class<?>, PropertyEditor> propertyEditors) {
- this.customEditors = propertyEditors;
- }
-
- protected static class Reference {
- private Object object;
- private String property;
- private String externalRef;
-
- public Reference(Object object, String property, String externalRef) {
- this.object = object;
- this.property = property;
- this.externalRef = externalRef;
- }
-
- public Object getObject() {
- return object;
- }
-
- public String getProperty() {
- return property;
- }
-
- public String getExternalRef() {
- return externalRef;
- }
-
- }
-}
+++ /dev/null
-/*
- * Copyright (C) 2007-2012 Argeo GmbH
- *
- * 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 org.argeo.server.dao;
-
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-import org.argeo.ArgeoException;
-
-public abstract class AbstractTabularDaoSupport extends
- AbstractMemoryDaoSupport {
- private final static Log log = LogFactory
- .getLog(AbstractTabularDaoSupport.class);
-
- private Map<String, List<Object>> tabularView = new HashMap<String, List<Object>>();
-
- @Override
- protected Object findInternalRef(Reference reference) {
- TabularInternalReference tabReference = (TabularInternalReference) reference;
- return getFromTabularView(tabReference.getTargetTableName(),
- tabReference.getTargetRow());
- }
-
- protected Object getFromTabularView(String tableName, Integer row) {
- return tabularView.get(tableName).get(row - 2);
- }
-
- protected void registerInTabularView(String tableName, Object object) {
- if (!tabularView.containsKey(tableName))
- tabularView.put(tableName, new ArrayList<Object>());
- tabularView.get(tableName).add(object);
- }
-
- protected Class<?> findClassToInstantiate(String tableName) {
- // TODO: ability to map sheet names and class names
- String className = tableName;
- Class<?> clss = null;
- try {
- clss = getClassLoader().loadClass(className);
- return clss;
- } catch (ClassNotFoundException e) {
- // silent
- }
-
- scannedPkgs: for (String pkg : getScannedPackages()) {
- try {
- clss = getClassLoader().loadClass(pkg.trim() + "." + className);
- break scannedPkgs;
- } catch (ClassNotFoundException e) {
- // silent
- if (log.isTraceEnabled())
- log.trace(e.getMessage());
- }
- }
-
- if (clss == null)
- throw new ArgeoException("Cannot find a class for table "
- + tableName);
-
- return clss;
- }
-
- protected static class TabularInternalReference extends Reference {
- private String targetTableName;
- private Integer targetRow;
-
- public TabularInternalReference(Object object, String property,
- String targetSheet, Integer targetRow) {
- super(object, property, null);
- this.targetTableName = targetSheet;
- this.targetRow = targetRow;
- }
-
- public String getTargetTableName() {
- return targetTableName;
- }
-
- public Integer getTargetRow() {
- return targetRow;
- }
-
- }
-}
+++ /dev/null
-/*
- * Copyright (C) 2007-2012 Argeo GmbH
- *
- * 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 org.argeo.server.dao;
-
-public interface LightDaoAware {
- public void setLightDaoSupport(LightDaoSupport lightDaoSupport);
-}
+++ /dev/null
-/*
- * Copyright (C) 2007-2012 Argeo GmbH
- *
- * 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 org.argeo.server.dao;
-
-import java.beans.PropertyEditorSupport;
-
-import org.argeo.ArgeoException;
-
-public class LightDaoPropertyEditor extends PropertyEditorSupport implements
- LightDaoAware {
- private LightDaoSupport lightDaoSupport;
-
- private Class<?> targetClass;
- /** Can be null */
- private String businessIdField;
-
- @Override
- public String getAsText() {
- return getValue().toString();
- }
-
- @Override
- public void setAsText(String text) throws IllegalArgumentException {
- if (targetClass == null)
- throw new ArgeoException("Target class cannot be null");
-
- if (businessIdField != null)
- setValue(lightDaoSupport.getByField(targetClass, businessIdField,
- text));
- else
- setValue(lightDaoSupport.getByKey(targetClass, text));
- }
-
- public void setLightDaoSupport(LightDaoSupport lightDaoSupport) {
- this.lightDaoSupport = lightDaoSupport;
- }
-
- public void setTargetClass(Class<?> targetClass) {
- this.targetClass = targetClass;
- }
-
- public void setBusinessIdField(String businessIdField) {
- this.businessIdField = businessIdField;
- }
-
-}
+++ /dev/null
-/*
- * Copyright (C) 2007-2012 Argeo GmbH
- *
- * 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 org.argeo.server.dao;
-
-import java.util.List;
-
-/** Minimal generic DAO for easy to implements objects <-> storage mapping. */
-public interface LightDaoSupport {
- /** Retrieve an object of a given type by its unique key. */
- public <T> T getByKey(Class<T> clss, Object key);
-
- /** Retrieve an object of a given type by the value of one of its fields. */
- public <T> T getByField(Class<T> clss, String field, Object value);
-
- /** List all objects, optionally filtering them (implementation dependent) */
- public <T> List<T> list(Class<T> clss, Object filter);
-
- /** Lis all supported object types. */
- public List<Class<?>> getSupportedClasses();
-
- /** Save or update an object */
- public void saveOrUpdate(Object key, Object value, Class<?> clss);
-}
+++ /dev/null
-/*
- * Copyright (C) 2007-2012 Argeo GmbH
- *
- * 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 org.argeo.server.mvc;
-
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-import org.argeo.server.ServerAnswer;
-import org.springframework.web.servlet.HandlerExceptionResolver;
-import org.springframework.web.servlet.ModelAndView;
-
-public class DefaultHandlerExceptionResolver implements
- HandlerExceptionResolver {
- private final static Log log = LogFactory
- .getLog(DefaultHandlerExceptionResolver.class);
-
- public ModelAndView resolveException(HttpServletRequest request,
- HttpServletResponse response, Object handler, Exception ex) {
- ModelAndView mv = new ModelAndView();
- ServerAnswer serverAnswer = ServerAnswer.error(ex);
- mv.addObject(serverAnswer);
-
- if (log.isDebugEnabled())
- log.error(serverAnswer);
-
- mv.setViewName("500");
- // response.setStatus(500);
- return mv;
- }
-
-}
+++ /dev/null
-/*\r
- * Copyright (C) 2007-2012 Argeo GmbH\r
- *\r
- * Licensed under the Apache License, Version 2.0 (the "License");\r
- * you may not use this file except in compliance with the License.\r
- * You may obtain a copy of the License at\r
- *\r
- * http://www.apache.org/licenses/LICENSE-2.0\r
- *\r
- * Unless required by applicable law or agreed to in writing, software\r
- * distributed under the License is distributed on an "AS IS" BASIS,\r
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
- * See the License for the specific language governing permissions and\r
- * limitations under the License.\r
- */\r
-package org.argeo.server.mvc;\r
-\r
-import javax.servlet.http.HttpServletRequest;\r
-import javax.servlet.http.HttpServletResponse;\r
-\r
-import org.springframework.web.servlet.ModelAndView;\r
-import org.springframework.web.servlet.mvc.ParameterizableViewController;\r
-\r
-/** Dummy controller which simply redirects to a view (typically a JSP)*/\r
-public class EmptyViewController extends ParameterizableViewController {\r
-\r
- protected ModelAndView handleRequestInternal(HttpServletRequest request,\r
- HttpServletResponse response) throws Exception {\r
- ModelAndView modelAndView = new ModelAndView();\r
- modelAndView.setViewName(getViewName());\r
- return modelAndView;\r
-\r
- }\r
-}\r
+++ /dev/null
-/*
- * Copyright (C) 2007-2012 Argeo GmbH
- *
- * 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 org.argeo.server.mvc;
-
-public interface MvcConstants {
- public final static String ANSWER_MODEL_KEY = "org.argeo.server.mvc.ANSWER";
- public final static String ANSWER_MODEL_KEY_AS_HTML = "org.argeo.server.mvc.ANSWER_AS_HTML";
-}
+++ /dev/null
-/*
- * Copyright (C) 2007-2012 Argeo GmbH
- *
- * 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 org.argeo.server.mvc;
-
-import java.util.Locale;
-import java.util.Map;
-
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
-import org.argeo.ArgeoException;
-import org.argeo.server.ServerAnswer;
-import org.argeo.server.ServerSerializer;
-import org.springframework.validation.BindingResult;
-import org.springframework.web.servlet.View;
-import org.springframework.web.servlet.view.AbstractView;
-
-/**
- * Can be used as a standalone {@link View} or using
- * {@link SerializingViewResolver}
- */
-public class SerializingView extends AbstractView implements MvcConstants {
- private final String viewName;
- private final Locale locale;
-
- private ServerSerializer serializer;
-
- public SerializingView() {
- this.viewName = null;
- this.locale = Locale.getDefault();
- }
-
- public SerializingView(String viewName, Locale locale,
- ServerSerializer serializer) {
- this.viewName = viewName;
- this.locale = locale;
- this.serializer = serializer;
- }
-
- @SuppressWarnings({ "rawtypes" })
- @Override
- protected void renderMergedOutputModel(Map model,
- HttpServletRequest request, HttpServletResponse response)
- throws Exception {
- Boolean serverAnswersAsHtml = model
- .containsKey(ANSWER_MODEL_KEY_AS_HTML);
-
- final Object answer = findAnswerInModel(model);
-
- if ((answer instanceof ServerAnswer) && serverAnswersAsHtml) {
- response.setContentType("text/html");
- ServerAnswer serverAnswer = (ServerAnswer) answer;
- response.getWriter().append("<pre>");
- response.getWriter().append(serverAnswer.getMessage());
- response.getWriter().append("</pre>");
- } else {
- serializer.serialize(answer, request, response);
- }
- }
-
- @SuppressWarnings("rawtypes")
- protected Object findAnswerInModel(Map model) {
- if (model.size() == 1) {
- return model.values().iterator().next();
- } else if (model.size() == 2) {
- boolean otherIsBindingResult = false;
- Object answerValue = null;
- for (Object value : model.values()) {
- if (value instanceof BindingResult)
- otherIsBindingResult = true;
- else
- answerValue = value;
- }
-
- if (otherIsBindingResult)
- return answerValue;
- }
-
- if (model.containsKey(ANSWER_MODEL_KEY)) {
- return model.get(ANSWER_MODEL_KEY);
- } else if (model.containsKey(ANSWER_MODEL_KEY_AS_HTML)) {
- return model.get(ANSWER_MODEL_KEY_AS_HTML);
- } else if (viewName != null && model.containsKey(viewName)) {
- return model.get(viewName);
- } else {
- if (model.size() == 0)
- throw new ArgeoException("Model is empty.");
- else
- throw new ArgeoException(
- "Model has a size different from 1. Specify a modelKey.");
- }
- }
-
- public String getViewName() {
- return viewName;
- }
-
- public Locale getLocale() {
- return locale;
- }
-
- public void setSerializer(ServerSerializer serializer) {
- this.serializer = serializer;
- }
-
-}
+++ /dev/null
-/*
- * Copyright (C) 2007-2012 Argeo GmbH
- *
- * 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 org.argeo.server.mvc;
-
-import java.util.Locale;
-
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-import org.argeo.server.ServerSerializer;
-import org.springframework.web.servlet.View;
-import org.springframework.web.servlet.view.AbstractCachingViewResolver;
-
-/**
- * Returns a {@link SerializingView} based on the underlying.
- */
-public class SerializingViewResolver extends AbstractCachingViewResolver {
- private final static Log log = LogFactory
- .getLog(SerializingViewResolver.class);
-
- private ServerSerializer serializer;
-
- @Override
- protected View loadView(String viewName, Locale locale) throws Exception {
- if (log.isTraceEnabled())
- log.trace("viewName=" + viewName);
- return new SerializingView(viewName, locale, serializer);
- }
-
- public void setSerializer(ServerSerializer serializer) {
- this.serializer = serializer;
- }
-
-}
+++ /dev/null
-<?xml version="1.0" encoding="UTF-8"?>
-<classpath>
- <classpathentry kind="src" path="src" />
- <classpathentry kind="con"
- path="org.eclipse.pde.core.requiredPlugins" />
- <classpathentry kind="con"
- path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6" />
- <classpathentry kind="output" path="bin" />
-</classpath>
+++ /dev/null
-<?xml version="1.0" encoding="UTF-8"?>
-<projectDescription>
- <name>org.argeo.server.jackrabbit</name>
- <comment></comment>
- <projects>
- </projects>
- <buildSpec>
- <buildCommand>
- <name>org.eclipse.jdt.core.javabuilder</name>
- <arguments>
- </arguments>
- </buildCommand>
- <buildCommand>
- <name>org.eclipse.pde.ManifestBuilder</name>
- <arguments>
- </arguments>
- </buildCommand>
- <buildCommand>
- <name>org.eclipse.pde.SchemaBuilder</name>
- <arguments>
- </arguments>
- </buildCommand>
- </buildSpec>
- <natures>
- <nature>org.eclipse.jdt.core.javanature</nature>
- <nature>org.eclipse.pde.PluginNature</nature>
- </natures>
-</projectDescription>
+++ /dev/null
-eclipse.preferences.version=1
-org.eclipse.jdt.core.compiler.annotation.inheritNullAnnotations=disabled
-org.eclipse.jdt.core.compiler.annotation.missingNonNullByDefaultAnnotation=ignore
-org.eclipse.jdt.core.compiler.annotation.nonnull=org.eclipse.jdt.annotation.NonNull
-org.eclipse.jdt.core.compiler.annotation.nonnullbydefault=org.eclipse.jdt.annotation.NonNullByDefault
-org.eclipse.jdt.core.compiler.annotation.nullable=org.eclipse.jdt.annotation.Nullable
-org.eclipse.jdt.core.compiler.annotation.nullanalysis=disabled
-org.eclipse.jdt.core.compiler.problem.annotationSuperInterface=warning
-org.eclipse.jdt.core.compiler.problem.autoboxing=ignore
-org.eclipse.jdt.core.compiler.problem.comparingIdentical=warning
-org.eclipse.jdt.core.compiler.problem.deadCode=warning
-org.eclipse.jdt.core.compiler.problem.deprecation=warning
-org.eclipse.jdt.core.compiler.problem.deprecationInDeprecatedCode=disabled
-org.eclipse.jdt.core.compiler.problem.deprecationWhenOverridingDeprecatedMethod=disabled
-org.eclipse.jdt.core.compiler.problem.discouragedReference=warning
-org.eclipse.jdt.core.compiler.problem.emptyStatement=ignore
-org.eclipse.jdt.core.compiler.problem.explicitlyClosedAutoCloseable=ignore
-org.eclipse.jdt.core.compiler.problem.fallthroughCase=ignore
-org.eclipse.jdt.core.compiler.problem.fatalOptionalError=disabled
-org.eclipse.jdt.core.compiler.problem.fieldHiding=ignore
-org.eclipse.jdt.core.compiler.problem.finalParameterBound=warning
-org.eclipse.jdt.core.compiler.problem.finallyBlockNotCompletingNormally=warning
-org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning
-org.eclipse.jdt.core.compiler.problem.hiddenCatchBlock=warning
-org.eclipse.jdt.core.compiler.problem.includeNullInfoFromAsserts=disabled
-org.eclipse.jdt.core.compiler.problem.incompatibleNonInheritedInterfaceMethod=warning
-org.eclipse.jdt.core.compiler.problem.incompleteEnumSwitch=warning
-org.eclipse.jdt.core.compiler.problem.indirectStaticAccess=ignore
-org.eclipse.jdt.core.compiler.problem.localVariableHiding=ignore
-org.eclipse.jdt.core.compiler.problem.methodWithConstructorName=warning
-org.eclipse.jdt.core.compiler.problem.missingDefaultCase=ignore
-org.eclipse.jdt.core.compiler.problem.missingDeprecatedAnnotation=ignore
-org.eclipse.jdt.core.compiler.problem.missingEnumCaseDespiteDefault=disabled
-org.eclipse.jdt.core.compiler.problem.missingHashCodeMethod=ignore
-org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotation=ignore
-org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotationForInterfaceMethodImplementation=enabled
-org.eclipse.jdt.core.compiler.problem.missingSerialVersion=warning
-org.eclipse.jdt.core.compiler.problem.missingSynchronizedOnInheritedMethod=ignore
-org.eclipse.jdt.core.compiler.problem.noEffectAssignment=warning
-org.eclipse.jdt.core.compiler.problem.noImplicitStringConversion=warning
-org.eclipse.jdt.core.compiler.problem.nonExternalizedStringLiteral=ignore
-org.eclipse.jdt.core.compiler.problem.nonnullParameterAnnotationDropped=warning
-org.eclipse.jdt.core.compiler.problem.nullAnnotationInferenceConflict=error
-org.eclipse.jdt.core.compiler.problem.nullReference=warning
-org.eclipse.jdt.core.compiler.problem.nullSpecViolation=error
-org.eclipse.jdt.core.compiler.problem.nullUncheckedConversion=warning
-org.eclipse.jdt.core.compiler.problem.overridingPackageDefaultMethod=warning
-org.eclipse.jdt.core.compiler.problem.parameterAssignment=ignore
-org.eclipse.jdt.core.compiler.problem.possibleAccidentalBooleanAssignment=ignore
-org.eclipse.jdt.core.compiler.problem.potentialNullReference=ignore
-org.eclipse.jdt.core.compiler.problem.potentiallyUnclosedCloseable=ignore
-org.eclipse.jdt.core.compiler.problem.rawTypeReference=warning
-org.eclipse.jdt.core.compiler.problem.redundantNullAnnotation=warning
-org.eclipse.jdt.core.compiler.problem.redundantNullCheck=ignore
-org.eclipse.jdt.core.compiler.problem.redundantSpecificationOfTypeArguments=ignore
-org.eclipse.jdt.core.compiler.problem.redundantSuperinterface=ignore
-org.eclipse.jdt.core.compiler.problem.reportMethodCanBePotentiallyStatic=ignore
-org.eclipse.jdt.core.compiler.problem.reportMethodCanBeStatic=ignore
-org.eclipse.jdt.core.compiler.problem.specialParameterHidingField=disabled
-org.eclipse.jdt.core.compiler.problem.staticAccessReceiver=warning
-org.eclipse.jdt.core.compiler.problem.suppressOptionalErrors=disabled
-org.eclipse.jdt.core.compiler.problem.suppressWarnings=enabled
-org.eclipse.jdt.core.compiler.problem.syntacticNullAnalysisForFields=disabled
-org.eclipse.jdt.core.compiler.problem.syntheticAccessEmulation=ignore
-org.eclipse.jdt.core.compiler.problem.typeParameterHiding=warning
-org.eclipse.jdt.core.compiler.problem.unavoidableGenericTypeProblems=enabled
-org.eclipse.jdt.core.compiler.problem.uncheckedTypeOperation=warning
-org.eclipse.jdt.core.compiler.problem.unclosedCloseable=warning
-org.eclipse.jdt.core.compiler.problem.undocumentedEmptyBlock=ignore
-org.eclipse.jdt.core.compiler.problem.unhandledWarningToken=warning
-org.eclipse.jdt.core.compiler.problem.unnecessaryElse=ignore
-org.eclipse.jdt.core.compiler.problem.unnecessaryTypeCheck=ignore
-org.eclipse.jdt.core.compiler.problem.unqualifiedFieldAccess=ignore
-org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownException=ignore
-org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionExemptExceptionAndThrowable=enabled
-org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionIncludeDocCommentReference=enabled
-org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionWhenOverriding=disabled
-org.eclipse.jdt.core.compiler.problem.unusedImport=warning
-org.eclipse.jdt.core.compiler.problem.unusedLabel=warning
-org.eclipse.jdt.core.compiler.problem.unusedLocal=warning
-org.eclipse.jdt.core.compiler.problem.unusedObjectAllocation=ignore
-org.eclipse.jdt.core.compiler.problem.unusedParameter=ignore
-org.eclipse.jdt.core.compiler.problem.unusedParameterIncludeDocCommentReference=enabled
-org.eclipse.jdt.core.compiler.problem.unusedParameterWhenImplementingAbstract=disabled
-org.eclipse.jdt.core.compiler.problem.unusedParameterWhenOverridingConcrete=disabled
-org.eclipse.jdt.core.compiler.problem.unusedPrivateMember=warning
-org.eclipse.jdt.core.compiler.problem.unusedTypeParameter=ignore
-org.eclipse.jdt.core.compiler.problem.unusedWarningToken=warning
-org.eclipse.jdt.core.compiler.problem.varargsArgumentNeedCast=warning
+++ /dev/null
-Import-Package: javax.servlet,\
-org.apache.jackrabbit.webdav.server,\
-org.osgi.framework;version="0.0.0",\
-org.xml.sax;version="0.0.0",\
-org.apache.jackrabbit.webdav.jcr,\
-org.springframework.beans,\
-*
+++ /dev/null
-source.. = src/
-additional.bundles = org.apache.jackrabbit.data
+++ /dev/null
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
- <modelVersion>4.0.0</modelVersion>
- <parent>
- <groupId>org.argeo.commons</groupId>
- <artifactId>argeo-commons</artifactId>
- <version>2.1.14-SNAPSHOT</version>
- <relativePath>..</relativePath>
- </parent>
- <artifactId>org.argeo.server.jackrabbit</artifactId>
- <name>Commons Server Jackrabbit</name>
- <dependencies>
- <dependency>
- <groupId>org.argeo.commons</groupId>
- <artifactId>org.argeo.util</artifactId>
- <version>2.1.14-SNAPSHOT</version>
- </dependency>
- <dependency>
- <groupId>org.argeo.commons</groupId>
- <artifactId>org.argeo.server.jcr</artifactId>
- <version>2.1.14-SNAPSHOT</version>
- </dependency>
-<!-- <dependency> -->
-<!-- <groupId>org.argeo.commons</groupId> -->
-<!-- <artifactId>org.argeo.server.jcr.mvc</artifactId> -->
-<!-- <version>2.1.13-SNAPSHOT</version> -->
-<!-- <scope>compile</scope> -->
-<!-- </dependency> -->
-
- <!-- Jackrabbit -->
-<!-- <dependency> -->
-<!-- <groupId>org.argeo.commons</groupId> -->
-<!-- <artifactId>org.argeo.dep.jackrabbit</artifactId> -->
-<!-- <type>pom</type> -->
-<!-- <version>2.1.12-SNAPSHOT</version> -->
-<!-- </dependency> -->
-
- <!-- OSGi -->
-<!-- <dependency> -->
-<!-- <groupId>org.argeo.tp.rap.platform</groupId> -->
-<!-- <artifactId>org.eclipse.osgi</artifactId> -->
-<!-- <scope>provided</scope> -->
-<!-- </dependency> -->
-
- <!-- Spring -->
-<!-- <dependency> -->
-<!-- <groupId>org.argeo.tp</groupId> -->
-<!-- <artifactId>org.springframework.beans</artifactId> -->
-<!-- </dependency> -->
-<!-- <dependency> -->
-<!-- <groupId>org.argeo.tp</groupId> -->
-<!-- <artifactId>org.springframework.web.servlet</artifactId> -->
-<!-- </dependency> -->
-<!-- <dependency> -->
-<!-- <groupId>org.argeo.tp</groupId> -->
-<!-- <artifactId>org.springframework.security.core</artifactId> -->
-<!-- </dependency> -->
-
- <!-- TEST -->
-<!-- <dependency> -->
-<!-- <groupId>org.argeo.commons</groupId> -->
-<!-- <artifactId>org.argeo.dep.log4j</artifactId> -->
-<!-- <version>2.1.12-SNAPSHOT</version> -->
-<!-- <type>pom</type> -->
-<!-- <scope>test</scope> -->
-<!-- </dependency> -->
- </dependencies>
-</project>
\ No newline at end of file
+++ /dev/null
-/*
- * Copyright (C) 2007-2012 Argeo GmbH
- *
- * 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 org.argeo.jackrabbit;
-
-import java.security.Principal;
-import java.util.Arrays;
-import java.util.List;
-
-import javax.jcr.RepositoryException;
-import javax.jcr.Session;
-
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-import org.apache.jackrabbit.api.JackrabbitSession;
-import org.apache.jackrabbit.api.security.user.Authorizable;
-import org.apache.jackrabbit.api.security.user.UserManager;
-import org.argeo.ArgeoException;
-import org.argeo.jcr.security.JcrAuthorizations;
-
-/** Apply authorizations to a Jackrabbit repository. */
-public class JackrabbitAuthorizations extends JcrAuthorizations {
- private final static Log log = LogFactory
- .getLog(JackrabbitAuthorizations.class);
-
- private List<String> groupPrefixes = Arrays
- .asList(new String[] { "ROLE_" });// new ArrayList<String>();
-
- @Override
- protected Principal getOrCreatePrincipal(Session session,
- String principalName) throws RepositoryException {
- UserManager um = ((JackrabbitSession) session).getUserManager();
- synchronized (um) {
- Authorizable authorizable = um.getAuthorizable(principalName);
- if (authorizable == null) {
- groupPrefixes: for (String groupPrefix : groupPrefixes) {
- if (principalName.startsWith(groupPrefix)) {
- authorizable = um.createGroup(principalName);
- log.info("Created group " + principalName);
- break groupPrefixes;
- }
- }
- if (authorizable == null)
- throw new ArgeoException("Authorizable " + principalName
- + " not found");
- }
- return authorizable.getPrincipal();
- }
- }
-
- public void setGroupPrefixes(List<String> groupsToCreate) {
- this.groupPrefixes = groupsToCreate;
- }
-}
+++ /dev/null
-/*
- * Copyright (C) 2007-2012 Argeo GmbH
- *
- * 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 org.argeo.jackrabbit;
-
-import java.io.File;
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.HashSet;
-import java.util.Map;
-import java.util.Properties;
-import java.util.Set;
-import java.util.TreeSet;
-import java.util.UUID;
-
-import javax.jcr.Node;
-import javax.jcr.Repository;
-import javax.jcr.RepositoryException;
-import javax.jcr.Session;
-
-import org.apache.commons.io.FileUtils;
-import org.apache.commons.io.IOUtils;
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-import org.apache.jackrabbit.api.JackrabbitRepository;
-import org.apache.jackrabbit.core.RepositoryImpl;
-import org.apache.jackrabbit.core.config.RepositoryConfig;
-import org.apache.jackrabbit.core.config.RepositoryConfigurationParser;
-import org.argeo.ArgeoException;
-import org.argeo.jcr.ArgeoNames;
-import org.argeo.jcr.JcrUtils;
-import org.argeo.jcr.MaintainedRepository;
-import org.springframework.core.io.Resource;
-import org.springframework.util.SystemPropertyUtils;
-import org.xml.sax.InputSource;
-
-/**
- * Wrapper around a Jackrabbit repository which allows to configure it in Spring
- * and expose it as a {@link Repository}.
- */
-public class JackrabbitContainer extends JackrabbitWrapper implements
- MaintainedRepository {
- private final static Log log = LogFactory.getLog(JackrabbitContainer.class);
-
- // local
- private Resource configuration;
-
- private Resource variables;
-
- private RepositoryConfig repositoryConfig;
- private File homeDirectory;
- private Boolean inMemory = false;
-
- /** Migrations to execute (if not already done) */
- private Set<JackrabbitDataModelMigration> dataModelMigrations = new HashSet<JackrabbitDataModelMigration>();
-
- /** Straight (non spring) values */
- private Properties configurationProperties;
- private InputSource configurationXml;
-
- /**
- * Empty constructor, {@link #init()} should be called after properties have
- * been set
- */
- public JackrabbitContainer() {
- }
-
- public void init() {
- long begin = System.currentTimeMillis();
-
- if (getRepository() != null)
- throw new ArgeoException(
- "Cannot be used to wrap another repository");
- Repository repository = createJackrabbitRepository();
- super.setRepository(repository);
-
- // migrate if needed
- migrate();
-
- // apply new CND files after migration
- prepareDataModel();
-
- double duration = ((double) (System.currentTimeMillis() - begin)) / 1000;
- if (log.isDebugEnabled())
- log.debug("Initialized JCR repository wrapper in " + duration
- + " s");
- }
-
- /** Actually creates the new repository. */
- protected Repository createJackrabbitRepository() {
- long begin = System.currentTimeMillis();
- InputStream configurationIn = null;
- Repository repository;
- try {
- // temporary
- if (inMemory && getHomeDirectory().exists()) {
- FileUtils.deleteDirectory(getHomeDirectory());
- log.warn("Deleted Jackrabbit home directory "
- + getHomeDirectory());
- }
-
- // process configuration file
- Properties vars = getConfigurationProperties();
- vars.put(RepositoryConfigurationParser.REPOSITORY_HOME_VARIABLE,
- getHomeDirectory().getCanonicalPath());
- InputSource is;
- if (configurationXml != null)
- is = configurationXml;
- else {
- configurationIn = readConfiguration();
- is = new InputSource(configurationIn);
- }
- repositoryConfig = RepositoryConfig.create(is, vars);
-
- //
- // Actual repository creation
- //
- repository = RepositoryImpl.create(repositoryConfig);
-
- double duration = ((double) (System.currentTimeMillis() - begin)) / 1000;
- if (log.isTraceEnabled())
- log.trace("Created Jackrabbit repository in " + duration
- + " s, home: " + getHomeDirectory());
-
- return repository;
- } catch (Exception e) {
- throw new ArgeoException("Cannot create Jackrabbit repository "
- + getHomeDirectory(), e);
- } finally {
- IOUtils.closeQuietly(configurationIn);
- }
- }
-
- /** Lazy init. */
- protected File getHomeDirectory() {
- try {
- if (homeDirectory == null) {
- if (inMemory) {
- homeDirectory = new File(
- System.getProperty("java.io.tmpdir")
- + File.separator
- + System.getProperty("user.name")
- + File.separator + "jackrabbit-"
- + UUID.randomUUID());
- homeDirectory.mkdirs();
- // will it work if directory is not empty??
- homeDirectory.deleteOnExit();
- }
- }
-
- return homeDirectory.getCanonicalFile();
- } catch (IOException e) {
- throw new ArgeoException("Cannot get canonical file for "
- + homeDirectory, e);
- }
- }
-
- /** Executes migrations, if needed. */
- protected void migrate() {
- // No migration to perform
- if (dataModelMigrations.size() == 0)
- return;
-
- Boolean restartAndClearCaches = false;
-
- // migrate data
- Session session = null;
- try {
- session = login();
- for (JackrabbitDataModelMigration dataModelMigration : new TreeSet<JackrabbitDataModelMigration>(
- dataModelMigrations)) {
- if (dataModelMigration.migrate(session)) {
- restartAndClearCaches = true;
- }
- }
- } catch (ArgeoException e) {
- throw e;
- } catch (Exception e) {
- throw new ArgeoException("Cannot migrate", e);
- } finally {
- JcrUtils.logoutQuietly(session);
- }
-
- // restart repository
- if (restartAndClearCaches) {
- Repository repository = getRepository();
- if (repository instanceof RepositoryImpl) {
- JackrabbitDataModelMigration
- .clearRepositoryCaches(((RepositoryImpl) repository)
- .getConfig());
- }
- ((JackrabbitRepository) repository).shutdown();
- createJackrabbitRepository();
- }
-
- // set data model version
- try {
- session = login();
- } catch (RepositoryException e) {
- throw new ArgeoException("Cannot login to migrated repository", e);
- }
-
- for (JackrabbitDataModelMigration dataModelMigration : new TreeSet<JackrabbitDataModelMigration>(
- dataModelMigrations)) {
- try {
- if (session.itemExists(dataModelMigration
- .getDataModelNodePath())) {
- Node dataModelNode = session.getNode(dataModelMigration
- .getDataModelNodePath());
- dataModelNode.setProperty(
- ArgeoNames.ARGEO_DATA_MODEL_VERSION,
- dataModelMigration.getTargetVersion());
- session.save();
- }
- } catch (Exception e) {
- log.error("Cannot set model version", e);
- }
- }
- JcrUtils.logoutQuietly(session);
-
- }
-
- /** Shutdown the repository */
- public void destroy() throws Exception {
- Repository repository = getRepository();
- if (repository != null && repository instanceof RepositoryImpl) {
- long begin = System.currentTimeMillis();
- ((RepositoryImpl) repository).shutdown();
- if (inMemory)
- if (getHomeDirectory().exists()) {
- FileUtils.deleteDirectory(getHomeDirectory());
- if (log.isDebugEnabled())
- log.debug("Deleted Jackrabbit home directory "
- + getHomeDirectory());
- }
- double duration = ((double) (System.currentTimeMillis() - begin)) / 1000;
- log.info("Destroyed Jackrabbit repository in " + duration
- + " s, home: " + getHomeDirectory());
- }
- repository = null;
- }
-
- public void dispose() {
- throw new IllegalArgumentException(
- "Call destroy() method instead of dispose()");
- }
-
- /*
- * UTILITIES
- */
- /**
- * Reads the configuration which will initialize a {@link RepositoryConfig}.
- */
- protected InputStream readConfiguration() {
- try {
- return configuration != null ? configuration.getInputStream()
- : null;
- } catch (IOException e) {
- throw new ArgeoException("Cannot read Jackrabbit configuration "
- + configuration, e);
- }
- }
-
- /**
- * Reads the variables which will initialize a {@link Properties}. Returns
- * null by default, to be overridden.
- *
- * @return a new stream or null if no variables available
- */
- protected InputStream readVariables() {
- try {
- return variables != null ? variables.getInputStream() : null;
- } catch (IOException e) {
- throw new ArgeoException("Cannot read Jackrabbit variables "
- + variables, e);
- }
- }
-
- /**
- * Resolves ${} placeholders in the provided string. Based on system
- * properties if no map is provided.
- */
- protected String resolvePlaceholders(String string,
- Map<String, String> variables) {
- return SystemPropertyUtils.resolvePlaceholders(string);
- }
-
- /** Generates the properties to use in the configuration. */
- protected Properties getConfigurationProperties() {
- if (configurationProperties != null)
- return configurationProperties;
-
- InputStream propsIn = null;
- Properties vars;
- try {
- vars = new Properties();
- propsIn = readVariables();
- if (propsIn != null) {
- vars.load(propsIn);
- }
- // resolve system properties
- for (Object key : vars.keySet()) {
- // TODO: implement a smarter mechanism to resolve nested ${}
- String newValue = resolvePlaceholders(
- vars.getProperty(key.toString()), null);
- vars.put(key, newValue);
- }
- // override with system properties
- vars.putAll(System.getProperties());
-
- if (log.isTraceEnabled()) {
- log.trace("Jackrabbit config variables:");
- for (Object key : new TreeSet<Object>(vars.keySet()))
- log.trace(key + "=" + vars.getProperty(key.toString()));
- }
-
- } catch (IOException e) {
- throw new ArgeoException("Cannot read configuration properties", e);
- } finally {
- IOUtils.closeQuietly(propsIn);
- }
- return vars;
- }
-
- /*
- * FIELDS ACCESS
- */
-
- public void setHomeDirectory(File homeDirectory) {
- this.homeDirectory = homeDirectory;
- }
-
- public void setInMemory(Boolean inMemory) {
- this.inMemory = inMemory;
- }
-
- public void setRepository(Repository repository) {
- throw new ArgeoException("Cannot be used to wrap another repository");
- }
-
- public void setDataModelMigrations(
- Set<JackrabbitDataModelMigration> dataModelMigrations) {
- this.dataModelMigrations = dataModelMigrations;
- }
-
- public void setVariables(Resource variables) {
- this.variables = variables;
- }
-
- public void setConfiguration(Resource configuration) {
- this.configuration = configuration;
- }
-
- public void setConfigurationProperties(Properties configurationProperties) {
- this.configurationProperties = configurationProperties;
- }
-
- public void setConfigurationXml(InputSource configurationXml) {
- this.configurationXml = configurationXml;
- }
-
-}
+++ /dev/null
-/*
- * Copyright (C) 2007-2012 Argeo GmbH
- *
- * 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 org.argeo.jackrabbit;
-
-import java.io.InputStreamReader;
-import java.io.Reader;
-
-import javax.jcr.Node;
-import javax.jcr.Session;
-
-import org.apache.commons.io.IOUtils;
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-import org.apache.jackrabbit.commons.cnd.CndImporter;
-import org.apache.jackrabbit.core.config.RepositoryConfig;
-import org.argeo.ArgeoException;
-import org.argeo.jcr.ArgeoNames;
-import org.argeo.jcr.JcrCallback;
-import org.argeo.jcr.JcrUtils;
-import org.springframework.core.io.Resource;
-
-/** Migrate the data in a Jackrabbit repository. */
-public class JackrabbitDataModelMigration implements
- Comparable<JackrabbitDataModelMigration> {
- private final static Log log = LogFactory
- .getLog(JackrabbitDataModelMigration.class);
-
- private String dataModelNodePath;
- private String targetVersion;
- private Resource migrationCnd;
- private JcrCallback dataModification;
-
- /**
- * Expects an already started repository with the old data model to migrate.
- * Expects to be run with admin rights (Repository.login() will be used).
- *
- * @return true if a migration was performed and the repository needs to be
- * restarted and its caches cleared.
- */
- public Boolean migrate(Session session) {
- long begin = System.currentTimeMillis();
- Reader reader = null;
- try {
- // check if already migrated
- if (!session.itemExists(dataModelNodePath)) {
- log.warn("Node " + dataModelNodePath
- + " does not exist: nothing to migrate.");
- return false;
- }
- Node dataModelNode = session.getNode(dataModelNodePath);
- if (dataModelNode.hasProperty(ArgeoNames.ARGEO_DATA_MODEL_VERSION)) {
- String currentVersion = dataModelNode.getProperty(
- ArgeoNames.ARGEO_DATA_MODEL_VERSION).getString();
- if (compareVersions(currentVersion, targetVersion) >= 0) {
- log.info("Data model at version " + currentVersion
- + ", no need to migrate.");
- return false;
- }
- }
-
- // apply transitional CND
- if (migrationCnd != null) {
- reader = new InputStreamReader(migrationCnd.getInputStream());
- CndImporter.registerNodeTypes(reader, session, true);
- session.save();
- log.info("Registered migration node types from " + migrationCnd);
- }
-
- // modify data
- dataModification.execute(session);
-
- // apply changes
- session.save();
-
- long duration = System.currentTimeMillis() - begin;
- log.info("Migration of data model " + dataModelNodePath + " to "
- + targetVersion + " performed in " + duration + "ms");
- return true;
- } catch (Exception e) {
- JcrUtils.discardQuietly(session);
- throw new ArgeoException("Migration of data model "
- + dataModelNodePath + " to " + targetVersion + " failed.",
- e);
- } finally {
- JcrUtils.logoutQuietly(session);
- IOUtils.closeQuietly(reader);
- }
- }
-
- protected static int compareVersions(String version1, String version2) {
- // TODO do a proper version analysis and comparison
- return version1.compareTo(version2);
- }
-
- /** To be called on a stopped repository. */
- public static void clearRepositoryCaches(RepositoryConfig repositoryConfig) {
- try {
- String customeNodeTypesPath = "/nodetypes/custom_nodetypes.xml";
- repositoryConfig.getFileSystem().deleteFile(customeNodeTypesPath);
- if (log.isDebugEnabled())
- log.debug("Cleared " + customeNodeTypesPath);
- } catch (Exception e) {
- throw new ArgeoException("Cannot clear caches", e);
- }
-
- // File customNodeTypes = new File(home.getPath()
- // + "/repository/nodetypes/custom_nodetypes.xml");
- // if (customNodeTypes.exists()) {
- // customNodeTypes.delete();
- // if (log.isDebugEnabled())
- // log.debug("Cleared " + customNodeTypes);
- // } else {
- // log.warn("File " + customNodeTypes + " not found.");
- // }
- }
-
- /*
- * FOR USE IN (SORTED) SETS
- */
-
- public int compareTo(JackrabbitDataModelMigration dataModelMigration) {
- // TODO make ordering smarter
- if (dataModelNodePath.equals(dataModelMigration.dataModelNodePath))
- return compareVersions(targetVersion,
- dataModelMigration.targetVersion);
- else
- return dataModelNodePath
- .compareTo(dataModelMigration.dataModelNodePath);
- }
-
- @Override
- public boolean equals(Object obj) {
- if (!(obj instanceof JackrabbitDataModelMigration))
- return false;
- JackrabbitDataModelMigration dataModelMigration = (JackrabbitDataModelMigration) obj;
- return dataModelNodePath.equals(dataModelMigration.dataModelNodePath)
- && targetVersion.equals(dataModelMigration.targetVersion);
- }
-
- @Override
- public int hashCode() {
- return targetVersion.hashCode();
- }
-
- public void setDataModelNodePath(String dataModelNodePath) {
- this.dataModelNodePath = dataModelNodePath;
- }
-
- public void setTargetVersion(String targetVersion) {
- this.targetVersion = targetVersion;
- }
-
- public void setMigrationCnd(Resource migrationCnd) {
- this.migrationCnd = migrationCnd;
- }
-
- public void setDataModification(JcrCallback dataModification) {
- this.dataModification = dataModification;
- }
-
- public String getDataModelNodePath() {
- return dataModelNodePath;
- }
-
- public String getTargetVersion() {
- return targetVersion;
- }
-
-}
+++ /dev/null
-/*
- * Copyright (C) 2007-2012 Argeo GmbH
- *
- * 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 org.argeo.jackrabbit;
-
-import java.io.File;
-import java.io.InputStream;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Properties;
-
-import javax.jcr.Repository;
-import javax.jcr.RepositoryException;
-import javax.jcr.RepositoryFactory;
-import javax.jcr.Session;
-
-import org.apache.commons.io.IOUtils;
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-import org.apache.jackrabbit.commons.JcrUtils;
-import org.apache.jackrabbit.core.RepositoryImpl;
-import org.apache.jackrabbit.core.config.RepositoryConfig;
-import org.apache.jackrabbit.core.config.RepositoryConfigurationParser;
-import org.apache.jackrabbit.jcr2dav.Jcr2davRepositoryFactory;
-import org.argeo.ArgeoException;
-import org.argeo.jcr.ArgeoJcrConstants;
-import org.argeo.jcr.DefaultRepositoryFactory;
-import org.springframework.core.io.ClassPathResource;
-import org.springframework.core.io.Resource;
-import org.xml.sax.InputSource;
-
-/**
- * Repository factory which can create new repositories and access remote
- * Jackrabbit repositories
- */
-public class JackrabbitRepositoryFactory extends DefaultRepositoryFactory
- implements RepositoryFactory, ArgeoJcrConstants {
-
- private final static Log log = LogFactory
- .getLog(JackrabbitRepositoryFactory.class);
-
- private Resource fileRepositoryConfiguration = new ClassPathResource(
- "/org/argeo/jackrabbit/repository-h2.xml");
-
- @SuppressWarnings({ "rawtypes", "unchecked" })
- public Repository getRepository(Map parameters) throws RepositoryException {
- // check if can be found by alias
- Repository repository = super.getRepository(parameters);
- if (repository != null)
- return repository;
-
- // check if remote
- String uri = null;
- if (parameters.containsKey(JCR_REPOSITORY_URI))
- uri = parameters.get(JCR_REPOSITORY_URI).toString();
- else if (parameters.containsKey(JcrUtils.REPOSITORY_URI))
- uri = parameters.get(JcrUtils.REPOSITORY_URI).toString();
-
- if (uri != null) {
- if (uri.startsWith("http"))// http, https
- repository = createRemoteRepository(uri);
- else if (uri.startsWith("file"))// http, https
- repository = createFileRepository(uri, parameters);
- else if (uri.startsWith("vm")) {
- log.warn("URI "
- + uri
- + " should have been managed by generic JCR repository factory");
- repository = getRepositoryByAlias(getAliasFromURI(uri));
- }
- }
-
- // publish under alias
- if (parameters.containsKey(JCR_REPOSITORY_ALIAS)) {
- Properties properties = new Properties();
- properties.putAll(parameters);
- String alias = parameters.get(JCR_REPOSITORY_ALIAS).toString();
- publish(alias, repository, properties);
- log.info("Registered JCR repository under alias '" + alias
- + "' with properties " + properties);
- }
-
- return repository;
- }
-
- protected Repository createRemoteRepository(String uri)
- throws RepositoryException {
- Map<String, String> params = new HashMap<String, String>();
- params.put(JcrUtils.REPOSITORY_URI, uri);
- Repository repository = new Jcr2davRepositoryFactory()
- .getRepository(params);
- if (repository == null)
- throw new ArgeoException("Remote Davex repository " + uri
- + " not found");
- log.info("Initialized remote Jackrabbit repository from uri " + uri);
- return repository;
- }
-
- @SuppressWarnings({ "rawtypes", "unchecked" })
- protected Repository createFileRepository(final String uri, Map parameters)
- throws RepositoryException {
- InputStream configurationIn = null;
- try {
- Properties vars = new Properties();
- vars.putAll(parameters);
- String dirPath = uri.substring("file:".length());
- File homeDir = new File(dirPath);
- if (homeDir.exists() && !homeDir.isDirectory())
- throw new ArgeoException("Repository home " + dirPath
- + " is not a directory");
- if (!homeDir.exists())
- homeDir.mkdirs();
- configurationIn = fileRepositoryConfiguration.getInputStream();
- vars.put(RepositoryConfigurationParser.REPOSITORY_HOME_VARIABLE,
- homeDir.getCanonicalPath());
- RepositoryConfig repositoryConfig = RepositoryConfig.create(
- new InputSource(configurationIn), vars);
-
- // TransientRepository repository = new
- // TransientRepository(repositoryConfig);
- final RepositoryImpl repository = RepositoryImpl
- .create(repositoryConfig);
- Session session = repository.login();
- // FIXME make it generic
- org.argeo.jcr.JcrUtils.addPrivilege(session, "/", "ROLE_ADMIN",
- "jcr:all");
- org.argeo.jcr.JcrUtils.logoutQuietly(session);
- Runtime.getRuntime().addShutdownHook(
- new Thread("Clean JCR repository " + uri) {
- public void run() {
- repository.shutdown();
- log.info("Destroyed repository " + uri);
- }
- });
- log.info("Initialized file Jackrabbit repository from uri " + uri);
- return repository;
- } catch (Exception e) {
- throw new ArgeoException("Cannot create repository " + uri, e);
- } finally {
- IOUtils.closeQuietly(configurationIn);
- }
- }
-
- /**
- * Called after the repository has been initialised. Does nothing by
- * default.
- */
- @SuppressWarnings("rawtypes")
- protected void postInitialization(Repository repository, Map parameters) {
-
- }
-
- public void setFileRepositoryConfiguration(
- Resource fileRepositoryConfiguration) {
- this.fileRepositoryConfiguration = fileRepositoryConfiguration;
- }
-
-}
+++ /dev/null
-/*
- * Copyright (C) 2007-2012 Argeo GmbH
- *
- * 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 org.argeo.jackrabbit;
-
-import java.io.ByteArrayInputStream;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.io.Reader;
-import java.net.URL;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-import javax.jcr.Credentials;
-import javax.jcr.Node;
-import javax.jcr.NodeIterator;
-import javax.jcr.Repository;
-import javax.jcr.Session;
-import javax.jcr.nodetype.NodeType;
-
-import org.apache.commons.io.FilenameUtils;
-import org.apache.commons.io.IOUtils;
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-import org.apache.jackrabbit.commons.NamespaceHelper;
-import org.apache.jackrabbit.commons.cnd.CndImporter;
-import org.argeo.ArgeoException;
-import org.argeo.jcr.ArgeoJcrConstants;
-import org.argeo.jcr.ArgeoNames;
-import org.argeo.jcr.ArgeoTypes;
-import org.argeo.jcr.JcrRepositoryWrapper;
-import org.argeo.jcr.JcrUtils;
-import org.argeo.util.security.DigestUtils;
-import org.osgi.framework.Bundle;
-import org.osgi.framework.BundleContext;
-import org.osgi.framework.ServiceReference;
-import org.osgi.service.packageadmin.ExportedPackage;
-import org.osgi.service.packageadmin.PackageAdmin;
-import org.springframework.context.ResourceLoaderAware;
-import org.springframework.core.io.Resource;
-import org.springframework.core.io.ResourceLoader;
-
-/**
- * Wrapper around a Jackrabbit repository which allows to simplify configuration
- * and intercept some actions. It exposes itself as a {@link Repository}.
- */
-public class JackrabbitWrapper extends JcrRepositoryWrapper implements
- ResourceLoaderAware {
- private final static Log log = LogFactory.getLog(JackrabbitWrapper.class);
- private final static String DIGEST_ALGORITHM = "MD5";
-
- // local
- private ResourceLoader resourceLoader;
-
- // data model
- /** Node type definitions in CND format */
- private List<String> cndFiles = new ArrayList<String>();
- /**
- * Always import CNDs. Useful during development of new data models. In
- * production, explicit migration processes should be used.
- */
- private Boolean forceCndImport = true;
-
- /** Namespaces to register: key is prefix, value namespace */
- private Map<String, String> namespaces = new HashMap<String, String>();
-
- private BundleContext bundleContext;
-
- /**
- * Explicitly set admin credentials used in initialization. Useful for
- * testing, in real applications authentication is rather dealt with
- * externally
- */
- private Credentials adminCredentials = null;
-
- /**
- * Empty constructor, {@link #init()} should be called after properties have
- * been set
- */
- public JackrabbitWrapper() {
- }
-
- @Override
- public void init() {
- prepareDataModel();
- }
-
- /*
- * DATA MODEL
- */
-
- /**
- * Import declared node type definitions and register namespaces. Tries to
- * update the node definitions if they have changed. In case of failures an
- * error will be logged but no exception will be thrown.
- */
- protected void prepareDataModel() {
- if ((cndFiles == null || cndFiles.size() == 0)
- && (namespaces == null || namespaces.size() == 0))
- return;
-
- Session session = null;
- try {
- session = login(adminCredentials);
- // register namespaces
- if (namespaces.size() > 0) {
- NamespaceHelper namespaceHelper = new NamespaceHelper(session);
- namespaceHelper.registerNamespaces(namespaces);
- }
-
- // load CND files from classpath or as URL
- for (String resUrl : cndFiles) {
- processCndFile(session, resUrl);
- }
- } catch (Exception e) {
- JcrUtils.discardQuietly(session);
- throw new ArgeoException("Cannot import node type definitions "
- + cndFiles, e);
- } finally {
- JcrUtils.logoutQuietly(session);
- }
-
- }
-
- protected void processCndFile(Session session, String resUrl) {
- Reader reader = null;
- try {
- // check existing data model nodes
- new NamespaceHelper(session).registerNamespace(ArgeoNames.ARGEO,
- ArgeoNames.ARGEO_NAMESPACE);
- if (!session.itemExists(ArgeoJcrConstants.DATA_MODELS_BASE_PATH))
- JcrUtils.mkdirs(session,
- ArgeoJcrConstants.DATA_MODELS_BASE_PATH);
- Node dataModels = session
- .getNode(ArgeoJcrConstants.DATA_MODELS_BASE_PATH);
- NodeIterator it = dataModels.getNodes();
- Node dataModel = null;
- while (it.hasNext()) {
- Node node = it.nextNode();
- if (node.getProperty(ArgeoNames.ARGEO_URI).getString()
- .equals(resUrl)) {
- dataModel = node;
- break;
- }
- }
-
- byte[] cndContent = readCndContent(resUrl);
- String newDigest = DigestUtils.digest(DIGEST_ALGORITHM, cndContent);
- Bundle bundle = findDataModelBundle(resUrl);
-
- String currentVersion = null;
- if (dataModel != null) {
- currentVersion = dataModel.getProperty(
- ArgeoNames.ARGEO_DATA_MODEL_VERSION).getString();
- if (dataModel.hasNode(Node.JCR_CONTENT)) {
- String oldDigest = JcrUtils.checksumFile(dataModel,
- DIGEST_ALGORITHM);
- if (oldDigest.equals(newDigest)) {
- if (log.isDebugEnabled())
- log.debug("Data model " + resUrl
- + " hasn't changed, keeping version "
- + currentVersion);
- return;
- }
- }
- }
-
- if (dataModel != null && !forceCndImport) {
- log.info("Data model "
- + resUrl
- + " has changed since version "
- + currentVersion
- + (bundle != null ? ": version " + bundle.getVersion()
- + ", bundle " + bundle.getSymbolicName() : ""));
- return;
- }
-
- reader = new InputStreamReader(new ByteArrayInputStream(cndContent));
- // actually imports the CND
- try {
- CndImporter.registerNodeTypes(reader, session, true);
- } catch (Exception e) {
- log.error("Cannot import data model " + resUrl, e);
- return;
- }
-
- if (dataModel != null && !dataModel.isNodeType(NodeType.NT_FILE)) {
- dataModel.remove();
- dataModel = null;
- }
-
- // FIXME: what if argeo.cnd would not be the first called on
- // a new repo? argeo:dataModel would not be found
- String fileName = FilenameUtils.getName(resUrl);
- if (dataModel == null) {
- dataModel = dataModels.addNode(fileName, NodeType.NT_FILE);
- dataModel.addNode(Node.JCR_CONTENT, NodeType.NT_RESOURCE);
- dataModel.addMixin(ArgeoTypes.ARGEO_DATA_MODEL);
- dataModel.setProperty(ArgeoNames.ARGEO_URI, resUrl);
- } else {
- session.getWorkspace().getVersionManager()
- .checkout(dataModel.getPath());
- }
- if (bundle != null)
- dataModel.setProperty(ArgeoNames.ARGEO_DATA_MODEL_VERSION,
- bundle.getVersion().toString());
- else
- dataModel.setProperty(ArgeoNames.ARGEO_DATA_MODEL_VERSION,
- "0.0.0");
- JcrUtils.copyBytesAsFile(dataModel.getParent(), fileName,
- cndContent);
- JcrUtils.updateLastModified(dataModel);
- session.save();
- session.getWorkspace().getVersionManager()
- .checkin(dataModel.getPath());
-
- if (currentVersion == null)
- log.info("Data model "
- + resUrl
- + (bundle != null ? ", version " + bundle.getVersion()
- + ", bundle " + bundle.getSymbolicName() : ""));
- else
- log.info("Data model "
- + resUrl
- + " updated from version "
- + currentVersion
- + (bundle != null ? ", version " + bundle.getVersion()
- + ", bundle " + bundle.getSymbolicName() : ""));
- } catch (Exception e) {
- throw new ArgeoException("Cannot process data model " + resUrl, e);
- } finally {
- IOUtils.closeQuietly(reader);
- }
- }
-
- protected byte[] readCndContent(String resUrl) {
- InputStream in = null;
- try {
- boolean classpath;
- // normalize URL
- if (bundleContext != null && resUrl.startsWith("classpath:")) {
- resUrl = resUrl.substring("classpath:".length());
- classpath = true;
- } else if (resUrl.indexOf(':') < 0) {
- if (!resUrl.startsWith("/")) {
- resUrl = "/" + resUrl;
- log.warn("Classpath should start with '/'");
- }
- classpath = true;
- } else {
- classpath = false;
- }
-
- URL url = null;
- if (classpath) {
- if (bundleContext != null) {
- Bundle currentBundle = bundleContext.getBundle();
- url = currentBundle.getResource(resUrl);
- } else {
- resUrl = "classpath:" + resUrl;
- url = null;
- }
- } else if (!resUrl.startsWith("classpath:")) {
- url = new URL(resUrl);
- }
-
- if (url != null) {
- in = url.openStream();
- } else if (resourceLoader != null) {
- Resource res = resourceLoader.getResource(resUrl);
- in = res.getInputStream();
- url = res.getURL();
- } else {
- throw new ArgeoException("No " + resUrl + " in the classpath,"
- + " make sure the containing" + " package is visible.");
- }
-
- return IOUtils.toByteArray(in);
- } catch (Exception e) {
- throw new ArgeoException("Cannot read CND from " + resUrl, e);
- } finally {
- IOUtils.closeQuietly(in);
- }
- }
-
- /*
- * REPOSITORY INTERCEPTOR
- */
-
- /*
- * UTILITIES
- */
- /** Find which OSGi bundle provided the data model resource */
- protected Bundle findDataModelBundle(String resUrl) {
- if (bundleContext == null)
- return null;
-
- if (resUrl.startsWith("/"))
- resUrl = resUrl.substring(1);
- String pkg = resUrl.substring(0, resUrl.lastIndexOf('/')).replace('/',
- '.');
- ServiceReference paSr = bundleContext
- .getServiceReference(PackageAdmin.class.getName());
- PackageAdmin packageAdmin = (PackageAdmin) bundleContext
- .getService(paSr);
-
- // find exported package
- ExportedPackage exportedPackage = null;
- ExportedPackage[] exportedPackages = packageAdmin
- .getExportedPackages(pkg);
- if (exportedPackages == null)
- throw new ArgeoException("No exported package found for " + pkg);
- for (ExportedPackage ep : exportedPackages) {
- for (Bundle b : ep.getImportingBundles()) {
- if (b.getBundleId() == bundleContext.getBundle().getBundleId()) {
- exportedPackage = ep;
- break;
- }
- }
- }
-
- Bundle exportingBundle = null;
- if (exportedPackage != null) {
- exportingBundle = exportedPackage.getExportingBundle();
- } else {
- // assume this is in the same bundle
- exportingBundle = bundleContext.getBundle();
- // throw new ArgeoException("No OSGi exporting package found for "
- // + resUrl);
- }
- return exportingBundle;
- }
-
- /*
- * FIELDS ACCESS
- */
- public void setNamespaces(Map<String, String> namespaces) {
- this.namespaces = namespaces;
- }
-
- public void setCndFiles(List<String> cndFiles) {
- this.cndFiles = cndFiles;
- }
-
- public void setBundleContext(BundleContext bundleContext) {
- this.bundleContext = bundleContext;
- }
-
- protected BundleContext getBundleContext() {
- return bundleContext;
- }
-
- public void setForceCndImport(Boolean forceCndUpdate) {
- this.forceCndImport = forceCndUpdate;
- }
-
- public void setResourceLoader(ResourceLoader resourceLoader) {
- this.resourceLoader = resourceLoader;
- }
-
- public void setAdminCredentials(Credentials adminCredentials) {
- this.adminCredentials = adminCredentials;
- }
-
-}
+++ /dev/null
-/*
- * Copyright (C) 2007-2012 Argeo GmbH
- *
- * 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 org.argeo.jackrabbit;
-
-import java.util.Hashtable;
-import java.util.Properties;
-
-import javax.jcr.Repository;
-
-import org.osgi.framework.BundleContext;
-
-/**
- * OSGi-aware Jackrabbit repository factory which can retrieve/publish
- * {@link Repository} as OSGi services.
- */
-public class OsgiJackrabbitRepositoryFactory extends
- JackrabbitRepositoryFactory {
- private BundleContext bundleContext;
-
- protected void publish(String alias, Repository repository,
- Properties properties) {
- if (bundleContext != null) {
- // do not modify reference
- Hashtable<String, String> props = new Hashtable<String, String>();
- props.putAll(props);
- props.put(JCR_REPOSITORY_ALIAS, alias);
- bundleContext.registerService(Repository.class.getName(),
- repository, props);
- }
- }
-
- public void setBundleContext(BundleContext bundleContext) {
- this.bundleContext = bundleContext;
- }
-
-}
+++ /dev/null
-<?xml version="1.0"?>
-<!DOCTYPE Repository PUBLIC "-//The Apache Software Foundation//DTD Jackrabbit 1.6//EN"
- "http://jackrabbit.apache.org/dtd/repository-2.0.dtd">
-<Repository>
- <!-- File system and datastore -->
- <FileSystem class="org.apache.jackrabbit.core.fs.local.LocalFileSystem">
- <param name="path" value="${rep.home}/fs" />
- </FileSystem>
- <DataStore class="org.apache.jackrabbit.core.data.FileDataStore">
- <param name="path" value="${rep.home}/datastore" />
- </DataStore>
-
- <!-- Workspace templates -->
- <Workspaces rootPath="${rep.home}/workspaces"
- defaultWorkspace="${argeo.node.repo.defaultWorkspace}" />
- <Workspace name="${wsp.name}">
- <FileSystem class="org.apache.jackrabbit.core.fs.local.LocalFileSystem">
- <param name="path" value="${wsp.home}" />
- </FileSystem>
- <PersistenceManager
- class="org.apache.jackrabbit.core.persistence.bundle.BundleFsPersistenceManager" />
- <SearchIndex class="org.apache.jackrabbit.core.query.lucene.SearchIndex">
- <param name="path" value="${wsp.home}/index" />
- </SearchIndex>
- </Workspace>
-
- <!-- Versioning -->
- <Versioning rootPath="${rep.home}/version">
- <FileSystem class="org.apache.jackrabbit.core.fs.local.LocalFileSystem">
- <param name="path" value="${rep.home}/version" />
- </FileSystem>
- <PersistenceManager
- class="org.apache.jackrabbit.core.persistence.bundle.BundleFsPersistenceManager" />
- </Versioning>
-
- <!-- Indexing -->
- <SearchIndex class="org.apache.jackrabbit.core.query.lucene.SearchIndex">
- <param name="path" value="${rep.home}/index" />
- <param name="extractorPoolSize" value="2" />
- <param name="supportHighlighting" value="true" />
- </SearchIndex>
-
- <!-- Security -->
- <Security appName="Jackrabbit">
- <SecurityManager class="org.argeo.security.jackrabbit.ArgeoSecurityManager"
- workspaceName="security">
- </SecurityManager>
- <AccessManager class="org.argeo.security.jackrabbit.ArgeoAccessManager">
- </AccessManager>
- <LoginModule class="org.argeo.security.jackrabbit.ArgeoLoginModule">
- </LoginModule>
- </Security>
-</Repository>
\ No newline at end of file
+++ /dev/null
-<?xml version="1.0"?>
-<!DOCTYPE Repository PUBLIC "-//The Apache Software Foundation//DTD Jackrabbit 1.6//EN"
- "http://jackrabbit.apache.org/dtd/repository-2.0.dtd">
-<Repository>
- <!-- Shared datasource -->
- <DataSources>
- <DataSource name="dataSource">
- <param name="driver" value="org.h2.Driver" />
- <param name="url" value="jdbc:h2:${rep.home}/h2/repository" />
- <param name="user" value="sa" />
- <param name="password" value="" />
- <param name="databaseType" value="h2" />
- <param name="maxPoolSize" value="10" />
- </DataSource>
- </DataSources>
-
- <!-- File system and datastore -->
- <FileSystem class="org.apache.jackrabbit.core.fs.db.DbFileSystem">
- <param name="dataSourceName" value="dataSource" />
- <param name="schema" value="default" />
- <param name="schemaObjectPrefix" value="fs_" />
- </FileSystem>
- <DataStore class="org.apache.jackrabbit.core.data.FileDataStore">
- <param name="path" value="${rep.home}/datastore" />
- </DataStore>
-
- <!-- Workspace templates -->
- <Workspaces rootPath="${rep.home}/workspaces"
- defaultWorkspace="main" />
- <Workspace name="${wsp.name}">
- <FileSystem class="org.apache.jackrabbit.core.fs.db.DbFileSystem">
- <param name="dataSourceName" value="dataSource" />
- <param name="schema" value="default" />
- <param name="schemaObjectPrefix" value="${wsp.name}_fs_" />
- </FileSystem>
- <PersistenceManager
- class="org.apache.jackrabbit.core.persistence.pool.H2PersistenceManager">
- <param name="dataSourceName" value="dataSource" />
- <param name="schemaObjectPrefix" value="${wsp.name}_pm_" />
- </PersistenceManager>
- <SearchIndex class="org.apache.jackrabbit.core.query.lucene.SearchIndex">
- <param name="path" value="${wsp.home}/index" />
- </SearchIndex>
- </Workspace>
-
- <!-- Versioning -->
- <Versioning rootPath="${rep.home}/version">
- <FileSystem class="org.apache.jackrabbit.core.fs.db.DbFileSystem">
- <param name="dataSourceName" value="dataSource" />
- <param name="schema" value="default" />
- <param name="schemaObjectPrefix" value="fs_ver_" />
- </FileSystem>
- <PersistenceManager
- class="org.apache.jackrabbit.core.persistence.pool.H2PersistenceManager">
- <param name="dataSourceName" value="dataSource" />
- <param name="schemaObjectPrefix" value="pm_ver_" />
- </PersistenceManager>
- </Versioning>
-
- <!-- Indexing -->
- <SearchIndex class="org.apache.jackrabbit.core.query.lucene.SearchIndex">
- <param name="path" value="${rep.home}/index" />
- <param name="extractorPoolSize" value="2" />
- <param name="supportHighlighting" value="true" />
- </SearchIndex>
-
- <!-- Security -->
- <Security appName="Jackrabbit">
- <SecurityManager class="org.argeo.security.jackrabbit.ArgeoSecurityManager"
- workspaceName="security">
- </SecurityManager>
- <AccessManager class="org.argeo.security.jackrabbit.ArgeoAccessManager">
- </AccessManager>
- <LoginModule class="org.argeo.security.jackrabbit.ArgeoLoginModule">
- </LoginModule>
- </Security>
-</Repository>
\ No newline at end of file
+++ /dev/null
-<?xml version="1.0"?>
-<!--
-
- Copyright (C) 2007-2012 Argeo GmbH
-
- 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.
-
--->
-<!DOCTYPE Repository PUBLIC "-//The Apache Software Foundation//DTD Jackrabbit 1.6//EN"
- "http://jackrabbit.apache.org/dtd/repository-2.0.dtd">
-<Repository>
- <!-- File system and datastore -->
- <FileSystem class="org.apache.jackrabbit.core.fs.mem.MemoryFileSystem" />
-
- <!-- Workspace templates -->
- <Workspaces rootPath="${rep.home}/workspaces"
- defaultWorkspace="main" configRootPath="/workspaces" />
- <Workspace name="${wsp.name}">
- <FileSystem class="org.apache.jackrabbit.core.fs.mem.MemoryFileSystem" />
- <PersistenceManager
- class="org.apache.jackrabbit.core.persistence.bundle.BundleFsPersistenceManager">
- <param name="blobFSBlockSize" value="1" />
- </PersistenceManager>
- <SearchIndex class="org.apache.jackrabbit.core.query.lucene.SearchIndex">
- <param name="path" value="${rep.home}/repository/index" />
- <param name="directoryManagerClass"
- value="org.apache.jackrabbit.core.query.lucene.directory.RAMDirectoryManager" />
- <FileSystem class="org.apache.jackrabbit.core.fs.mem.MemoryFileSystem" />
- </SearchIndex>
- </Workspace>
-
- <!-- Versioning -->
- <Versioning rootPath="${rep.home}/version">
- <FileSystem class="org.apache.jackrabbit.core.fs.mem.MemoryFileSystem" />
- <PersistenceManager
- class="org.apache.jackrabbit.core.persistence.bundle.BundleFsPersistenceManager">
- <param name="blobFSBlockSize" value="1" />
- </PersistenceManager>
- </Versioning>
-
- <!-- Indexing -->
- <SearchIndex class="org.apache.jackrabbit.core.query.lucene.SearchIndex">
- <param name="path" value="${rep.home}/repository/index" />
- <param name="directoryManagerClass"
- value="org.apache.jackrabbit.core.query.lucene.directory.RAMDirectoryManager" />
- <FileSystem class="org.apache.jackrabbit.core.fs.mem.MemoryFileSystem" />
- </SearchIndex>
-
- <!-- Security -->
- <Security appName="Jackrabbit">
- <SecurityManager class="org.argeo.security.jackrabbit.ArgeoSecurityManager"
- workspaceName="security">
- </SecurityManager>
- <AccessManager class="org.argeo.security.jackrabbit.ArgeoAccessManager">
- </AccessManager>
- <LoginModule class="org.argeo.security.jackrabbit.ArgeoLoginModule">
- </LoginModule>
- </Security>
-</Repository>
\ No newline at end of file
+++ /dev/null
-/*
- * Copyright (C) 2007-2012 Argeo GmbH
- *
- * 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 org.argeo.jackrabbit.servlet;
-
-import java.io.Serializable;
-
-import javax.jcr.LoginException;
-import javax.jcr.Repository;
-import javax.jcr.RepositoryException;
-import javax.jcr.Session;
-import javax.servlet.ServletException;
-import javax.servlet.http.HttpServletRequest;
-
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-import org.apache.jackrabbit.server.SessionProvider;
-import org.argeo.jcr.JcrUtils;
-
-/**
- * Implements an open session in view patter: a new JCR session is created for
- * each request
- */
-public class OpenInViewSessionProvider implements SessionProvider, Serializable {
- private static final long serialVersionUID = 2270957712453841368L;
-
- private final static Log log = LogFactory
- .getLog(OpenInViewSessionProvider.class);
-
- public Session getSession(HttpServletRequest request, Repository rep,
- String workspace) throws LoginException, ServletException,
- RepositoryException {
- return login(request, rep, workspace);
- }
-
- protected Session login(HttpServletRequest request, Repository repository,
- String workspace) throws RepositoryException {
- if (log.isTraceEnabled())
- log.trace("Login to workspace "
- + (workspace == null ? "<default>" : workspace)
- + " in web session " + request.getSession().getId());
- return repository.login(workspace);
- }
-
- public void releaseSession(Session session) {
- JcrUtils.logoutQuietly(session);
- if (log.isTraceEnabled())
- log.trace("Logged out remote JCR session " + session);
- }
-
- public void init() {
- }
-
- public void destroy() {
- }
-
-}
+++ /dev/null
-/*
- * Copyright (C) 2007-2012 Argeo GmbH
- *
- * 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 org.argeo.jackrabbit.servlet;
-
-import javax.jcr.Repository;
-
-import org.apache.jackrabbit.server.SessionProvider;
-import org.apache.jackrabbit.server.remoting.davex.JcrRemotingServlet;
-
-/** Provides remote access to a JCR repository */
-public class RemotingServlet extends JcrRemotingServlet {
- public final static String INIT_PARAM_RESOURCE_PATH_PREFIX = JcrRemotingServlet.INIT_PARAM_RESOURCE_PATH_PREFIX;
- public final static String INIT_PARAM_HOME = JcrRemotingServlet.INIT_PARAM_HOME;
- public final static String INIT_PARAM_TMP_DIRECTORY = JcrRemotingServlet.INIT_PARAM_TMP_DIRECTORY;
-
- private static final long serialVersionUID = 3131835511468341309L;
-
- private final Repository repository;
- private final SessionProvider sessionProvider;
-
- public RemotingServlet(Repository repository,
- SessionProvider sessionProvider) {
- this.repository = repository;
- this.sessionProvider = sessionProvider;
- }
-
- @Override
- protected Repository getRepository() {
- return repository;
- }
-
- @Override
- protected SessionProvider getSessionProvider() {
- return sessionProvider;
- }
-
-}
+++ /dev/null
-/*
- * Copyright (C) 2007-2012 Argeo GmbH
- *
- * 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 org.argeo.jackrabbit.servlet;
-
-import java.io.IOException;
-
-import javax.jcr.Repository;
-import javax.servlet.ServletException;
-
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-import org.apache.jackrabbit.server.SessionProvider;
-import org.apache.jackrabbit.webdav.DavException;
-import org.apache.jackrabbit.webdav.DavResource;
-import org.apache.jackrabbit.webdav.WebdavRequest;
-import org.apache.jackrabbit.webdav.WebdavResponse;
-import org.apache.jackrabbit.webdav.simple.SimpleWebdavServlet;
-
-/** WebDav servlet whose repository is injected */
-public class WebdavServlet extends SimpleWebdavServlet {
- public final static String INIT_PARAM_RESOURCE_CONFIG = SimpleWebdavServlet.INIT_PARAM_RESOURCE_CONFIG;
- public final static String INIT_PARAM_RESOURCE_PATH_PREFIX = SimpleWebdavServlet.INIT_PARAM_RESOURCE_PATH_PREFIX;
-
- private static final long serialVersionUID = -369787931175177080L;
-
- private final static Log log = LogFactory.getLog(WebdavServlet.class);
-
- private final Repository repository;
-
- public WebdavServlet(Repository repository, SessionProvider sessionProvider) {
- this.repository = repository;
- setSessionProvider(sessionProvider);
- }
-
- public Repository getRepository() {
- return repository;
- }
-
- @Override
- protected boolean execute(WebdavRequest request, WebdavResponse response,
- int method, DavResource resource) throws ServletException,
- IOException, DavException {
- if (log.isTraceEnabled())
- log.trace(request.getMethod() + "\t" + request.getPathInfo());
- boolean res = super.execute(request, response, method, resource);
- return res;
- }
-
-}
org.springframework.core;resolution:=optional,\
org.springframework.core.io;resolution:=optional,\
org.springframework.*;resolution:=optional,\
+org.apache.jackrabbit.*;resolution:=optional,\
+org.apache.jackrabbit.webdav.jcr;resolution:=optional,\
+org.apache.jackrabbit.webdav.server;resolution:=optional,\
*
+Export-Package: org.argeo.jcr.*, org.argeo.jackrabbit.*
\ No newline at end of file
source.. = src/,\
ext/test/
-output.. = bin/,\
- bin/test-classes/
+output.. = bin/
bin.includes = META-INF/,\
.
additional.bundles = org.junit,\
org.apache.jackrabbit.jcr.commons,\
org.apache.jackrabbit.spi,\
org.apache.jackrabbit.spi.commons
-
<relativePath>..</relativePath>
</parent>
<artifactId>org.argeo.server.jcr</artifactId>
- <name>Commons Server JCR</name>
- <build>
- <plugins>
- <plugin>
- <groupId>org.argeo.maven.plugins</groupId>
- <artifactId>maven-argeo-osgi-plugin</artifactId>
- <configuration>
- <useDependencies>true</useDependencies>
- <onlyCheck>true</onlyCheck>
- <argsToAppend>
- <arg>-clean</arg>
- </argsToAppend>
- <systemProperties>
- <!-- Make sure that no bundle will be started -->
- <argeo.osgi.start>XXX</argeo.osgi.start>
- </systemProperties>
- </configuration>
- </plugin>
- </plugins>
- </build>
+ <name>Commons JCR</name>
<dependencies>
<dependency>
<groupId>org.argeo.commons</groupId>
<artifactId>org.argeo.util</artifactId>
<version>2.1.14-SNAPSHOT</version>
</dependency>
-
-<!-- <dependency> -->
-<!-- <groupId>org.argeo.tp</groupId> -->
-<!-- <artifactId>argeo-tp</artifactId> -->
-<!-- <version>${version.argeo-distribution}</version> -->
-<!-- <type>pom</type> -->
-<!-- <scope>provided</scope> -->
-<!-- </dependency> -->
-
- <!-- JCR -->
-<!-- <dependency> -->
-<!-- <groupId>org.argeo.tp</groupId> -->
-<!-- <artifactId>javax.jcr</artifactId> -->
-<!-- </dependency> -->
-
-<!-- <dependency> -->
-<!-- <groupId>org.argeo.tp</groupId> -->
-<!-- <artifactId>org.apache.commons.io</artifactId> -->
-<!-- </dependency> -->
-
- <!-- Spring -->
-<!-- <dependency> -->
-<!-- <groupId>org.argeo.tp</groupId> -->
-<!-- <artifactId>org.springframework.core</artifactId> -->
-<!-- <scope>provided</scope> -->
-<!-- </dependency> -->
-<!-- <dependency> -->
-<!-- <groupId>org.argeo.tp</groupId> -->
-<!-- <artifactId>org.springframework.beans</artifactId> -->
-<!-- <scope>provided</scope> -->
-<!-- </dependency> -->
-
- <!-- OSGi -->
-<!-- <dependency> -->
-<!-- <groupId>org.argeo.tp</groupId> -->
-<!-- <artifactId>org.eclipse.osgi</artifactId> -->
-<!-- <scope>provided</scope> -->
-<!-- </dependency> -->
-
- <!-- Logging -->
-<!-- <dependency> -->
-<!-- <groupId>org.argeo.tp</groupId> -->
-<!-- <artifactId>slf4j.org.apache.commons.logging</artifactId> -->
-<!-- </dependency> -->
-
-
- <!-- TEST -->
-<!-- <dependency> -->
-<!-- <groupId>org.argeo.tp</groupId> -->
-<!-- <artifactId>junit</artifactId> -->
-<!-- <optional>true</optional> -->
-<!-- </dependency> -->
-<!-- <dependency> -->
-<!-- <groupId>org.argeo.commons</groupId> -->
-<!-- <artifactId>org.argeo.dep.jackrabbit</artifactId> -->
-<!-- <version>2.1.12-SNAPSHOT</version> -->
-<!-- <type>pom</type> -->
-<!-- <scope>test</scope> -->
-<!-- </dependency> -->
-<!-- <dependency> -->
-<!-- <groupId>org.argeo.commons</groupId> -->
-<!-- <artifactId>org.argeo.dep.log4j</artifactId> -->
-<!-- <version>2.1.12-SNAPSHOT</version> -->
-<!-- <type>pom</type> -->
-<!-- <scope>test</scope> -->
-<!-- </dependency> -->
-<!-- <dependency> -->
-<!-- <groupId>org.argeo.commons</groupId> -->
-<!-- <artifactId>org.argeo.osgi.boot</artifactId> -->
-<!-- <version>2.1.12-SNAPSHOT</version> -->
-<!-- <scope>test</scope> -->
-<!-- </dependency> -->
</dependencies>
</project>
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (C) 2007-2012 Argeo GmbH
+ *
+ * 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 org.argeo.jackrabbit;
+
+import java.security.Principal;
+import java.util.Arrays;
+import java.util.List;
+
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.jackrabbit.api.JackrabbitSession;
+import org.apache.jackrabbit.api.security.user.Authorizable;
+import org.apache.jackrabbit.api.security.user.UserManager;
+import org.argeo.ArgeoException;
+import org.argeo.jcr.security.JcrAuthorizations;
+
+/** Apply authorizations to a Jackrabbit repository. */
+public class JackrabbitAuthorizations extends JcrAuthorizations {
+ private final static Log log = LogFactory
+ .getLog(JackrabbitAuthorizations.class);
+
+ private List<String> groupPrefixes = Arrays
+ .asList(new String[] { "ROLE_" });// new ArrayList<String>();
+
+ @Override
+ protected Principal getOrCreatePrincipal(Session session,
+ String principalName) throws RepositoryException {
+ UserManager um = ((JackrabbitSession) session).getUserManager();
+ synchronized (um) {
+ Authorizable authorizable = um.getAuthorizable(principalName);
+ if (authorizable == null) {
+ groupPrefixes: for (String groupPrefix : groupPrefixes) {
+ if (principalName.startsWith(groupPrefix)) {
+ authorizable = um.createGroup(principalName);
+ log.info("Created group " + principalName);
+ break groupPrefixes;
+ }
+ }
+ if (authorizable == null)
+ throw new ArgeoException("Authorizable " + principalName
+ + " not found");
+ }
+ return authorizable.getPrincipal();
+ }
+ }
+
+ public void setGroupPrefixes(List<String> groupsToCreate) {
+ this.groupPrefixes = groupsToCreate;
+ }
+}
--- /dev/null
+/*
+ * Copyright (C) 2007-2012 Argeo GmbH
+ *
+ * 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 org.argeo.jackrabbit;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Properties;
+import java.util.Set;
+import java.util.TreeSet;
+import java.util.UUID;
+
+import javax.jcr.Node;
+import javax.jcr.Repository;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+
+import org.apache.commons.io.FileUtils;
+import org.apache.commons.io.IOUtils;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.jackrabbit.api.JackrabbitRepository;
+import org.apache.jackrabbit.core.RepositoryImpl;
+import org.apache.jackrabbit.core.config.RepositoryConfig;
+import org.apache.jackrabbit.core.config.RepositoryConfigurationParser;
+import org.argeo.ArgeoException;
+import org.argeo.jcr.ArgeoNames;
+import org.argeo.jcr.JcrUtils;
+import org.argeo.jcr.MaintainedRepository;
+import org.springframework.core.io.Resource;
+import org.springframework.util.SystemPropertyUtils;
+import org.xml.sax.InputSource;
+
+/**
+ * Wrapper around a Jackrabbit repository which allows to configure it in Spring
+ * and expose it as a {@link Repository}.
+ */
+public class JackrabbitContainer extends JackrabbitWrapper implements
+ MaintainedRepository {
+ private final static Log log = LogFactory.getLog(JackrabbitContainer.class);
+
+ // local
+ private Resource configuration;
+
+ private Resource variables;
+
+ private RepositoryConfig repositoryConfig;
+ private File homeDirectory;
+ private Boolean inMemory = false;
+
+ /** Migrations to execute (if not already done) */
+ private Set<JackrabbitDataModelMigration> dataModelMigrations = new HashSet<JackrabbitDataModelMigration>();
+
+ /** Straight (non spring) values */
+ private Properties configurationProperties;
+ private InputSource configurationXml;
+
+ /**
+ * Empty constructor, {@link #init()} should be called after properties have
+ * been set
+ */
+ public JackrabbitContainer() {
+ }
+
+ public void init() {
+ long begin = System.currentTimeMillis();
+
+ if (getRepository() != null)
+ throw new ArgeoException(
+ "Cannot be used to wrap another repository");
+ Repository repository = createJackrabbitRepository();
+ super.setRepository(repository);
+
+ // migrate if needed
+ migrate();
+
+ // apply new CND files after migration
+ prepareDataModel();
+
+ double duration = ((double) (System.currentTimeMillis() - begin)) / 1000;
+ if (log.isDebugEnabled())
+ log.debug("Initialized JCR repository wrapper in " + duration
+ + " s");
+ }
+
+ /** Actually creates the new repository. */
+ protected Repository createJackrabbitRepository() {
+ long begin = System.currentTimeMillis();
+ InputStream configurationIn = null;
+ Repository repository;
+ try {
+ // temporary
+ if (inMemory && getHomeDirectory().exists()) {
+ FileUtils.deleteDirectory(getHomeDirectory());
+ log.warn("Deleted Jackrabbit home directory "
+ + getHomeDirectory());
+ }
+
+ // process configuration file
+ Properties vars = getConfigurationProperties();
+ vars.put(RepositoryConfigurationParser.REPOSITORY_HOME_VARIABLE,
+ getHomeDirectory().getCanonicalPath());
+ InputSource is;
+ if (configurationXml != null)
+ is = configurationXml;
+ else {
+ configurationIn = readConfiguration();
+ is = new InputSource(configurationIn);
+ }
+ repositoryConfig = RepositoryConfig.create(is, vars);
+
+ //
+ // Actual repository creation
+ //
+ repository = RepositoryImpl.create(repositoryConfig);
+
+ double duration = ((double) (System.currentTimeMillis() - begin)) / 1000;
+ if (log.isTraceEnabled())
+ log.trace("Created Jackrabbit repository in " + duration
+ + " s, home: " + getHomeDirectory());
+
+ return repository;
+ } catch (Exception e) {
+ throw new ArgeoException("Cannot create Jackrabbit repository "
+ + getHomeDirectory(), e);
+ } finally {
+ IOUtils.closeQuietly(configurationIn);
+ }
+ }
+
+ /** Lazy init. */
+ protected File getHomeDirectory() {
+ try {
+ if (homeDirectory == null) {
+ if (inMemory) {
+ homeDirectory = new File(
+ System.getProperty("java.io.tmpdir")
+ + File.separator
+ + System.getProperty("user.name")
+ + File.separator + "jackrabbit-"
+ + UUID.randomUUID());
+ homeDirectory.mkdirs();
+ // will it work if directory is not empty??
+ homeDirectory.deleteOnExit();
+ }
+ }
+
+ return homeDirectory.getCanonicalFile();
+ } catch (IOException e) {
+ throw new ArgeoException("Cannot get canonical file for "
+ + homeDirectory, e);
+ }
+ }
+
+ /** Executes migrations, if needed. */
+ protected void migrate() {
+ // No migration to perform
+ if (dataModelMigrations.size() == 0)
+ return;
+
+ Boolean restartAndClearCaches = false;
+
+ // migrate data
+ Session session = null;
+ try {
+ session = login();
+ for (JackrabbitDataModelMigration dataModelMigration : new TreeSet<JackrabbitDataModelMigration>(
+ dataModelMigrations)) {
+ if (dataModelMigration.migrate(session)) {
+ restartAndClearCaches = true;
+ }
+ }
+ } catch (ArgeoException e) {
+ throw e;
+ } catch (Exception e) {
+ throw new ArgeoException("Cannot migrate", e);
+ } finally {
+ JcrUtils.logoutQuietly(session);
+ }
+
+ // restart repository
+ if (restartAndClearCaches) {
+ Repository repository = getRepository();
+ if (repository instanceof RepositoryImpl) {
+ JackrabbitDataModelMigration
+ .clearRepositoryCaches(((RepositoryImpl) repository)
+ .getConfig());
+ }
+ ((JackrabbitRepository) repository).shutdown();
+ createJackrabbitRepository();
+ }
+
+ // set data model version
+ try {
+ session = login();
+ } catch (RepositoryException e) {
+ throw new ArgeoException("Cannot login to migrated repository", e);
+ }
+
+ for (JackrabbitDataModelMigration dataModelMigration : new TreeSet<JackrabbitDataModelMigration>(
+ dataModelMigrations)) {
+ try {
+ if (session.itemExists(dataModelMigration
+ .getDataModelNodePath())) {
+ Node dataModelNode = session.getNode(dataModelMigration
+ .getDataModelNodePath());
+ dataModelNode.setProperty(
+ ArgeoNames.ARGEO_DATA_MODEL_VERSION,
+ dataModelMigration.getTargetVersion());
+ session.save();
+ }
+ } catch (Exception e) {
+ log.error("Cannot set model version", e);
+ }
+ }
+ JcrUtils.logoutQuietly(session);
+
+ }
+
+ /** Shutdown the repository */
+ public void destroy() throws Exception {
+ Repository repository = getRepository();
+ if (repository != null && repository instanceof RepositoryImpl) {
+ long begin = System.currentTimeMillis();
+ ((RepositoryImpl) repository).shutdown();
+ if (inMemory)
+ if (getHomeDirectory().exists()) {
+ FileUtils.deleteDirectory(getHomeDirectory());
+ if (log.isDebugEnabled())
+ log.debug("Deleted Jackrabbit home directory "
+ + getHomeDirectory());
+ }
+ double duration = ((double) (System.currentTimeMillis() - begin)) / 1000;
+ log.info("Destroyed Jackrabbit repository in " + duration
+ + " s, home: " + getHomeDirectory());
+ }
+ repository = null;
+ }
+
+ public void dispose() {
+ throw new IllegalArgumentException(
+ "Call destroy() method instead of dispose()");
+ }
+
+ /*
+ * UTILITIES
+ */
+ /**
+ * Reads the configuration which will initialize a {@link RepositoryConfig}.
+ */
+ protected InputStream readConfiguration() {
+ try {
+ return configuration != null ? configuration.getInputStream()
+ : null;
+ } catch (IOException e) {
+ throw new ArgeoException("Cannot read Jackrabbit configuration "
+ + configuration, e);
+ }
+ }
+
+ /**
+ * Reads the variables which will initialize a {@link Properties}. Returns
+ * null by default, to be overridden.
+ *
+ * @return a new stream or null if no variables available
+ */
+ protected InputStream readVariables() {
+ try {
+ return variables != null ? variables.getInputStream() : null;
+ } catch (IOException e) {
+ throw new ArgeoException("Cannot read Jackrabbit variables "
+ + variables, e);
+ }
+ }
+
+ /**
+ * Resolves ${} placeholders in the provided string. Based on system
+ * properties if no map is provided.
+ */
+ protected String resolvePlaceholders(String string,
+ Map<String, String> variables) {
+ return SystemPropertyUtils.resolvePlaceholders(string);
+ }
+
+ /** Generates the properties to use in the configuration. */
+ protected Properties getConfigurationProperties() {
+ if (configurationProperties != null)
+ return configurationProperties;
+
+ InputStream propsIn = null;
+ Properties vars;
+ try {
+ vars = new Properties();
+ propsIn = readVariables();
+ if (propsIn != null) {
+ vars.load(propsIn);
+ }
+ // resolve system properties
+ for (Object key : vars.keySet()) {
+ // TODO: implement a smarter mechanism to resolve nested ${}
+ String newValue = resolvePlaceholders(
+ vars.getProperty(key.toString()), null);
+ vars.put(key, newValue);
+ }
+ // override with system properties
+ vars.putAll(System.getProperties());
+
+ if (log.isTraceEnabled()) {
+ log.trace("Jackrabbit config variables:");
+ for (Object key : new TreeSet<Object>(vars.keySet()))
+ log.trace(key + "=" + vars.getProperty(key.toString()));
+ }
+
+ } catch (IOException e) {
+ throw new ArgeoException("Cannot read configuration properties", e);
+ } finally {
+ IOUtils.closeQuietly(propsIn);
+ }
+ return vars;
+ }
+
+ /*
+ * FIELDS ACCESS
+ */
+
+ public void setHomeDirectory(File homeDirectory) {
+ this.homeDirectory = homeDirectory;
+ }
+
+ public void setInMemory(Boolean inMemory) {
+ this.inMemory = inMemory;
+ }
+
+ public void setRepository(Repository repository) {
+ throw new ArgeoException("Cannot be used to wrap another repository");
+ }
+
+ public void setDataModelMigrations(
+ Set<JackrabbitDataModelMigration> dataModelMigrations) {
+ this.dataModelMigrations = dataModelMigrations;
+ }
+
+ public void setVariables(Resource variables) {
+ this.variables = variables;
+ }
+
+ public void setConfiguration(Resource configuration) {
+ this.configuration = configuration;
+ }
+
+ public void setConfigurationProperties(Properties configurationProperties) {
+ this.configurationProperties = configurationProperties;
+ }
+
+ public void setConfigurationXml(InputSource configurationXml) {
+ this.configurationXml = configurationXml;
+ }
+
+}
--- /dev/null
+/*
+ * Copyright (C) 2007-2012 Argeo GmbH
+ *
+ * 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 org.argeo.jackrabbit;
+
+import java.io.InputStreamReader;
+import java.io.Reader;
+
+import javax.jcr.Node;
+import javax.jcr.Session;
+
+import org.apache.commons.io.IOUtils;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.jackrabbit.commons.cnd.CndImporter;
+import org.apache.jackrabbit.core.config.RepositoryConfig;
+import org.argeo.ArgeoException;
+import org.argeo.jcr.ArgeoNames;
+import org.argeo.jcr.JcrCallback;
+import org.argeo.jcr.JcrUtils;
+import org.springframework.core.io.Resource;
+
+/** Migrate the data in a Jackrabbit repository. */
+public class JackrabbitDataModelMigration implements
+ Comparable<JackrabbitDataModelMigration> {
+ private final static Log log = LogFactory
+ .getLog(JackrabbitDataModelMigration.class);
+
+ private String dataModelNodePath;
+ private String targetVersion;
+ private Resource migrationCnd;
+ private JcrCallback dataModification;
+
+ /**
+ * Expects an already started repository with the old data model to migrate.
+ * Expects to be run with admin rights (Repository.login() will be used).
+ *
+ * @return true if a migration was performed and the repository needs to be
+ * restarted and its caches cleared.
+ */
+ public Boolean migrate(Session session) {
+ long begin = System.currentTimeMillis();
+ Reader reader = null;
+ try {
+ // check if already migrated
+ if (!session.itemExists(dataModelNodePath)) {
+ log.warn("Node " + dataModelNodePath
+ + " does not exist: nothing to migrate.");
+ return false;
+ }
+ Node dataModelNode = session.getNode(dataModelNodePath);
+ if (dataModelNode.hasProperty(ArgeoNames.ARGEO_DATA_MODEL_VERSION)) {
+ String currentVersion = dataModelNode.getProperty(
+ ArgeoNames.ARGEO_DATA_MODEL_VERSION).getString();
+ if (compareVersions(currentVersion, targetVersion) >= 0) {
+ log.info("Data model at version " + currentVersion
+ + ", no need to migrate.");
+ return false;
+ }
+ }
+
+ // apply transitional CND
+ if (migrationCnd != null) {
+ reader = new InputStreamReader(migrationCnd.getInputStream());
+ CndImporter.registerNodeTypes(reader, session, true);
+ session.save();
+ log.info("Registered migration node types from " + migrationCnd);
+ }
+
+ // modify data
+ dataModification.execute(session);
+
+ // apply changes
+ session.save();
+
+ long duration = System.currentTimeMillis() - begin;
+ log.info("Migration of data model " + dataModelNodePath + " to "
+ + targetVersion + " performed in " + duration + "ms");
+ return true;
+ } catch (Exception e) {
+ JcrUtils.discardQuietly(session);
+ throw new ArgeoException("Migration of data model "
+ + dataModelNodePath + " to " + targetVersion + " failed.",
+ e);
+ } finally {
+ JcrUtils.logoutQuietly(session);
+ IOUtils.closeQuietly(reader);
+ }
+ }
+
+ protected static int compareVersions(String version1, String version2) {
+ // TODO do a proper version analysis and comparison
+ return version1.compareTo(version2);
+ }
+
+ /** To be called on a stopped repository. */
+ public static void clearRepositoryCaches(RepositoryConfig repositoryConfig) {
+ try {
+ String customeNodeTypesPath = "/nodetypes/custom_nodetypes.xml";
+ repositoryConfig.getFileSystem().deleteFile(customeNodeTypesPath);
+ if (log.isDebugEnabled())
+ log.debug("Cleared " + customeNodeTypesPath);
+ } catch (Exception e) {
+ throw new ArgeoException("Cannot clear caches", e);
+ }
+
+ // File customNodeTypes = new File(home.getPath()
+ // + "/repository/nodetypes/custom_nodetypes.xml");
+ // if (customNodeTypes.exists()) {
+ // customNodeTypes.delete();
+ // if (log.isDebugEnabled())
+ // log.debug("Cleared " + customNodeTypes);
+ // } else {
+ // log.warn("File " + customNodeTypes + " not found.");
+ // }
+ }
+
+ /*
+ * FOR USE IN (SORTED) SETS
+ */
+
+ public int compareTo(JackrabbitDataModelMigration dataModelMigration) {
+ // TODO make ordering smarter
+ if (dataModelNodePath.equals(dataModelMigration.dataModelNodePath))
+ return compareVersions(targetVersion,
+ dataModelMigration.targetVersion);
+ else
+ return dataModelNodePath
+ .compareTo(dataModelMigration.dataModelNodePath);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (!(obj instanceof JackrabbitDataModelMigration))
+ return false;
+ JackrabbitDataModelMigration dataModelMigration = (JackrabbitDataModelMigration) obj;
+ return dataModelNodePath.equals(dataModelMigration.dataModelNodePath)
+ && targetVersion.equals(dataModelMigration.targetVersion);
+ }
+
+ @Override
+ public int hashCode() {
+ return targetVersion.hashCode();
+ }
+
+ public void setDataModelNodePath(String dataModelNodePath) {
+ this.dataModelNodePath = dataModelNodePath;
+ }
+
+ public void setTargetVersion(String targetVersion) {
+ this.targetVersion = targetVersion;
+ }
+
+ public void setMigrationCnd(Resource migrationCnd) {
+ this.migrationCnd = migrationCnd;
+ }
+
+ public void setDataModification(JcrCallback dataModification) {
+ this.dataModification = dataModification;
+ }
+
+ public String getDataModelNodePath() {
+ return dataModelNodePath;
+ }
+
+ public String getTargetVersion() {
+ return targetVersion;
+ }
+
+}
--- /dev/null
+/*
+ * Copyright (C) 2007-2012 Argeo GmbH
+ *
+ * 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 org.argeo.jackrabbit;
+
+import java.io.File;
+import java.io.InputStream;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Properties;
+
+import javax.jcr.Repository;
+import javax.jcr.RepositoryException;
+import javax.jcr.RepositoryFactory;
+import javax.jcr.Session;
+
+import org.apache.commons.io.IOUtils;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.jackrabbit.commons.JcrUtils;
+import org.apache.jackrabbit.core.RepositoryImpl;
+import org.apache.jackrabbit.core.config.RepositoryConfig;
+import org.apache.jackrabbit.core.config.RepositoryConfigurationParser;
+import org.apache.jackrabbit.jcr2dav.Jcr2davRepositoryFactory;
+import org.argeo.ArgeoException;
+import org.argeo.jcr.ArgeoJcrConstants;
+import org.argeo.jcr.DefaultRepositoryFactory;
+import org.springframework.core.io.ClassPathResource;
+import org.springframework.core.io.Resource;
+import org.xml.sax.InputSource;
+
+/**
+ * Repository factory which can create new repositories and access remote
+ * Jackrabbit repositories
+ */
+public class JackrabbitRepositoryFactory extends DefaultRepositoryFactory
+ implements RepositoryFactory, ArgeoJcrConstants {
+
+ private final static Log log = LogFactory
+ .getLog(JackrabbitRepositoryFactory.class);
+
+ private Resource fileRepositoryConfiguration = new ClassPathResource(
+ "/org/argeo/jackrabbit/repository-h2.xml");
+
+ @SuppressWarnings({ "rawtypes", "unchecked" })
+ public Repository getRepository(Map parameters) throws RepositoryException {
+ // check if can be found by alias
+ Repository repository = super.getRepository(parameters);
+ if (repository != null)
+ return repository;
+
+ // check if remote
+ String uri = null;
+ if (parameters.containsKey(JCR_REPOSITORY_URI))
+ uri = parameters.get(JCR_REPOSITORY_URI).toString();
+ else if (parameters.containsKey(JcrUtils.REPOSITORY_URI))
+ uri = parameters.get(JcrUtils.REPOSITORY_URI).toString();
+
+ if (uri != null) {
+ if (uri.startsWith("http"))// http, https
+ repository = createRemoteRepository(uri);
+ else if (uri.startsWith("file"))// http, https
+ repository = createFileRepository(uri, parameters);
+ else if (uri.startsWith("vm")) {
+ log.warn("URI "
+ + uri
+ + " should have been managed by generic JCR repository factory");
+ repository = getRepositoryByAlias(getAliasFromURI(uri));
+ }
+ }
+
+ // publish under alias
+ if (parameters.containsKey(JCR_REPOSITORY_ALIAS)) {
+ Properties properties = new Properties();
+ properties.putAll(parameters);
+ String alias = parameters.get(JCR_REPOSITORY_ALIAS).toString();
+ publish(alias, repository, properties);
+ log.info("Registered JCR repository under alias '" + alias
+ + "' with properties " + properties);
+ }
+
+ return repository;
+ }
+
+ protected Repository createRemoteRepository(String uri)
+ throws RepositoryException {
+ Map<String, String> params = new HashMap<String, String>();
+ params.put(JcrUtils.REPOSITORY_URI, uri);
+ Repository repository = new Jcr2davRepositoryFactory()
+ .getRepository(params);
+ if (repository == null)
+ throw new ArgeoException("Remote Davex repository " + uri
+ + " not found");
+ log.info("Initialized remote Jackrabbit repository from uri " + uri);
+ return repository;
+ }
+
+ @SuppressWarnings({ "rawtypes", "unchecked" })
+ protected Repository createFileRepository(final String uri, Map parameters)
+ throws RepositoryException {
+ InputStream configurationIn = null;
+ try {
+ Properties vars = new Properties();
+ vars.putAll(parameters);
+ String dirPath = uri.substring("file:".length());
+ File homeDir = new File(dirPath);
+ if (homeDir.exists() && !homeDir.isDirectory())
+ throw new ArgeoException("Repository home " + dirPath
+ + " is not a directory");
+ if (!homeDir.exists())
+ homeDir.mkdirs();
+ configurationIn = fileRepositoryConfiguration.getInputStream();
+ vars.put(RepositoryConfigurationParser.REPOSITORY_HOME_VARIABLE,
+ homeDir.getCanonicalPath());
+ RepositoryConfig repositoryConfig = RepositoryConfig.create(
+ new InputSource(configurationIn), vars);
+
+ // TransientRepository repository = new
+ // TransientRepository(repositoryConfig);
+ final RepositoryImpl repository = RepositoryImpl
+ .create(repositoryConfig);
+ Session session = repository.login();
+ // FIXME make it generic
+ org.argeo.jcr.JcrUtils.addPrivilege(session, "/", "ROLE_ADMIN",
+ "jcr:all");
+ org.argeo.jcr.JcrUtils.logoutQuietly(session);
+ Runtime.getRuntime().addShutdownHook(
+ new Thread("Clean JCR repository " + uri) {
+ public void run() {
+ repository.shutdown();
+ log.info("Destroyed repository " + uri);
+ }
+ });
+ log.info("Initialized file Jackrabbit repository from uri " + uri);
+ return repository;
+ } catch (Exception e) {
+ throw new ArgeoException("Cannot create repository " + uri, e);
+ } finally {
+ IOUtils.closeQuietly(configurationIn);
+ }
+ }
+
+ /**
+ * Called after the repository has been initialised. Does nothing by
+ * default.
+ */
+ @SuppressWarnings("rawtypes")
+ protected void postInitialization(Repository repository, Map parameters) {
+
+ }
+
+ public void setFileRepositoryConfiguration(
+ Resource fileRepositoryConfiguration) {
+ this.fileRepositoryConfiguration = fileRepositoryConfiguration;
+ }
+
+}
--- /dev/null
+/*
+ * Copyright (C) 2007-2012 Argeo GmbH
+ *
+ * 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 org.argeo.jackrabbit;
+
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.jcr.Credentials;
+import javax.jcr.Node;
+import javax.jcr.NodeIterator;
+import javax.jcr.Repository;
+import javax.jcr.Session;
+import javax.jcr.nodetype.NodeType;
+
+import org.apache.commons.io.FilenameUtils;
+import org.apache.commons.io.IOUtils;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.jackrabbit.commons.NamespaceHelper;
+import org.apache.jackrabbit.commons.cnd.CndImporter;
+import org.argeo.ArgeoException;
+import org.argeo.jcr.ArgeoJcrConstants;
+import org.argeo.jcr.ArgeoNames;
+import org.argeo.jcr.ArgeoTypes;
+import org.argeo.jcr.JcrRepositoryWrapper;
+import org.argeo.jcr.JcrUtils;
+import org.argeo.util.security.DigestUtils;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceReference;
+import org.osgi.service.packageadmin.ExportedPackage;
+import org.osgi.service.packageadmin.PackageAdmin;
+import org.springframework.context.ResourceLoaderAware;
+import org.springframework.core.io.Resource;
+import org.springframework.core.io.ResourceLoader;
+
+/**
+ * Wrapper around a Jackrabbit repository which allows to simplify configuration
+ * and intercept some actions. It exposes itself as a {@link Repository}.
+ */
+public class JackrabbitWrapper extends JcrRepositoryWrapper implements
+ ResourceLoaderAware {
+ private final static Log log = LogFactory.getLog(JackrabbitWrapper.class);
+ private final static String DIGEST_ALGORITHM = "MD5";
+
+ // local
+ private ResourceLoader resourceLoader;
+
+ // data model
+ /** Node type definitions in CND format */
+ private List<String> cndFiles = new ArrayList<String>();
+ /**
+ * Always import CNDs. Useful during development of new data models. In
+ * production, explicit migration processes should be used.
+ */
+ private Boolean forceCndImport = true;
+
+ /** Namespaces to register: key is prefix, value namespace */
+ private Map<String, String> namespaces = new HashMap<String, String>();
+
+ private BundleContext bundleContext;
+
+ /**
+ * Explicitly set admin credentials used in initialization. Useful for
+ * testing, in real applications authentication is rather dealt with
+ * externally
+ */
+ private Credentials adminCredentials = null;
+
+ /**
+ * Empty constructor, {@link #init()} should be called after properties have
+ * been set
+ */
+ public JackrabbitWrapper() {
+ }
+
+ @Override
+ public void init() {
+ prepareDataModel();
+ }
+
+ /*
+ * DATA MODEL
+ */
+
+ /**
+ * Import declared node type definitions and register namespaces. Tries to
+ * update the node definitions if they have changed. In case of failures an
+ * error will be logged but no exception will be thrown.
+ */
+ protected void prepareDataModel() {
+ if ((cndFiles == null || cndFiles.size() == 0)
+ && (namespaces == null || namespaces.size() == 0))
+ return;
+
+ Session session = null;
+ try {
+ session = login(adminCredentials);
+ // register namespaces
+ if (namespaces.size() > 0) {
+ NamespaceHelper namespaceHelper = new NamespaceHelper(session);
+ namespaceHelper.registerNamespaces(namespaces);
+ }
+
+ // load CND files from classpath or as URL
+ for (String resUrl : cndFiles) {
+ processCndFile(session, resUrl);
+ }
+ } catch (Exception e) {
+ JcrUtils.discardQuietly(session);
+ throw new ArgeoException("Cannot import node type definitions "
+ + cndFiles, e);
+ } finally {
+ JcrUtils.logoutQuietly(session);
+ }
+
+ }
+
+ protected void processCndFile(Session session, String resUrl) {
+ Reader reader = null;
+ try {
+ // check existing data model nodes
+ new NamespaceHelper(session).registerNamespace(ArgeoNames.ARGEO,
+ ArgeoNames.ARGEO_NAMESPACE);
+ if (!session.itemExists(ArgeoJcrConstants.DATA_MODELS_BASE_PATH))
+ JcrUtils.mkdirs(session,
+ ArgeoJcrConstants.DATA_MODELS_BASE_PATH);
+ Node dataModels = session
+ .getNode(ArgeoJcrConstants.DATA_MODELS_BASE_PATH);
+ NodeIterator it = dataModels.getNodes();
+ Node dataModel = null;
+ while (it.hasNext()) {
+ Node node = it.nextNode();
+ if (node.getProperty(ArgeoNames.ARGEO_URI).getString()
+ .equals(resUrl)) {
+ dataModel = node;
+ break;
+ }
+ }
+
+ byte[] cndContent = readCndContent(resUrl);
+ String newDigest = DigestUtils.digest(DIGEST_ALGORITHM, cndContent);
+ Bundle bundle = findDataModelBundle(resUrl);
+
+ String currentVersion = null;
+ if (dataModel != null) {
+ currentVersion = dataModel.getProperty(
+ ArgeoNames.ARGEO_DATA_MODEL_VERSION).getString();
+ if (dataModel.hasNode(Node.JCR_CONTENT)) {
+ String oldDigest = JcrUtils.checksumFile(dataModel,
+ DIGEST_ALGORITHM);
+ if (oldDigest.equals(newDigest)) {
+ if (log.isDebugEnabled())
+ log.debug("Data model " + resUrl
+ + " hasn't changed, keeping version "
+ + currentVersion);
+ return;
+ }
+ }
+ }
+
+ if (dataModel != null && !forceCndImport) {
+ log.info("Data model "
+ + resUrl
+ + " has changed since version "
+ + currentVersion
+ + (bundle != null ? ": version " + bundle.getVersion()
+ + ", bundle " + bundle.getSymbolicName() : ""));
+ return;
+ }
+
+ reader = new InputStreamReader(new ByteArrayInputStream(cndContent));
+ // actually imports the CND
+ try {
+ CndImporter.registerNodeTypes(reader, session, true);
+ } catch (Exception e) {
+ log.error("Cannot import data model " + resUrl, e);
+ return;
+ }
+
+ if (dataModel != null && !dataModel.isNodeType(NodeType.NT_FILE)) {
+ dataModel.remove();
+ dataModel = null;
+ }
+
+ // FIXME: what if argeo.cnd would not be the first called on
+ // a new repo? argeo:dataModel would not be found
+ String fileName = FilenameUtils.getName(resUrl);
+ if (dataModel == null) {
+ dataModel = dataModels.addNode(fileName, NodeType.NT_FILE);
+ dataModel.addNode(Node.JCR_CONTENT, NodeType.NT_RESOURCE);
+ dataModel.addMixin(ArgeoTypes.ARGEO_DATA_MODEL);
+ dataModel.setProperty(ArgeoNames.ARGEO_URI, resUrl);
+ } else {
+ session.getWorkspace().getVersionManager()
+ .checkout(dataModel.getPath());
+ }
+ if (bundle != null)
+ dataModel.setProperty(ArgeoNames.ARGEO_DATA_MODEL_VERSION,
+ bundle.getVersion().toString());
+ else
+ dataModel.setProperty(ArgeoNames.ARGEO_DATA_MODEL_VERSION,
+ "0.0.0");
+ JcrUtils.copyBytesAsFile(dataModel.getParent(), fileName,
+ cndContent);
+ JcrUtils.updateLastModified(dataModel);
+ session.save();
+ session.getWorkspace().getVersionManager()
+ .checkin(dataModel.getPath());
+
+ if (currentVersion == null)
+ log.info("Data model "
+ + resUrl
+ + (bundle != null ? ", version " + bundle.getVersion()
+ + ", bundle " + bundle.getSymbolicName() : ""));
+ else
+ log.info("Data model "
+ + resUrl
+ + " updated from version "
+ + currentVersion
+ + (bundle != null ? ", version " + bundle.getVersion()
+ + ", bundle " + bundle.getSymbolicName() : ""));
+ } catch (Exception e) {
+ throw new ArgeoException("Cannot process data model " + resUrl, e);
+ } finally {
+ IOUtils.closeQuietly(reader);
+ }
+ }
+
+ protected byte[] readCndContent(String resUrl) {
+ InputStream in = null;
+ try {
+ boolean classpath;
+ // normalize URL
+ if (bundleContext != null && resUrl.startsWith("classpath:")) {
+ resUrl = resUrl.substring("classpath:".length());
+ classpath = true;
+ } else if (resUrl.indexOf(':') < 0) {
+ if (!resUrl.startsWith("/")) {
+ resUrl = "/" + resUrl;
+ log.warn("Classpath should start with '/'");
+ }
+ classpath = true;
+ } else {
+ classpath = false;
+ }
+
+ URL url = null;
+ if (classpath) {
+ if (bundleContext != null) {
+ Bundle currentBundle = bundleContext.getBundle();
+ url = currentBundle.getResource(resUrl);
+ } else {
+ resUrl = "classpath:" + resUrl;
+ url = null;
+ }
+ } else if (!resUrl.startsWith("classpath:")) {
+ url = new URL(resUrl);
+ }
+
+ if (url != null) {
+ in = url.openStream();
+ } else if (resourceLoader != null) {
+ Resource res = resourceLoader.getResource(resUrl);
+ in = res.getInputStream();
+ url = res.getURL();
+ } else {
+ throw new ArgeoException("No " + resUrl + " in the classpath,"
+ + " make sure the containing" + " package is visible.");
+ }
+
+ return IOUtils.toByteArray(in);
+ } catch (Exception e) {
+ throw new ArgeoException("Cannot read CND from " + resUrl, e);
+ } finally {
+ IOUtils.closeQuietly(in);
+ }
+ }
+
+ /*
+ * REPOSITORY INTERCEPTOR
+ */
+
+ /*
+ * UTILITIES
+ */
+ /** Find which OSGi bundle provided the data model resource */
+ protected Bundle findDataModelBundle(String resUrl) {
+ if (bundleContext == null)
+ return null;
+
+ if (resUrl.startsWith("/"))
+ resUrl = resUrl.substring(1);
+ String pkg = resUrl.substring(0, resUrl.lastIndexOf('/')).replace('/',
+ '.');
+ ServiceReference paSr = bundleContext
+ .getServiceReference(PackageAdmin.class.getName());
+ PackageAdmin packageAdmin = (PackageAdmin) bundleContext
+ .getService(paSr);
+
+ // find exported package
+ ExportedPackage exportedPackage = null;
+ ExportedPackage[] exportedPackages = packageAdmin
+ .getExportedPackages(pkg);
+ if (exportedPackages == null)
+ throw new ArgeoException("No exported package found for " + pkg);
+ for (ExportedPackage ep : exportedPackages) {
+ for (Bundle b : ep.getImportingBundles()) {
+ if (b.getBundleId() == bundleContext.getBundle().getBundleId()) {
+ exportedPackage = ep;
+ break;
+ }
+ }
+ }
+
+ Bundle exportingBundle = null;
+ if (exportedPackage != null) {
+ exportingBundle = exportedPackage.getExportingBundle();
+ } else {
+ // assume this is in the same bundle
+ exportingBundle = bundleContext.getBundle();
+ // throw new ArgeoException("No OSGi exporting package found for "
+ // + resUrl);
+ }
+ return exportingBundle;
+ }
+
+ /*
+ * FIELDS ACCESS
+ */
+ public void setNamespaces(Map<String, String> namespaces) {
+ this.namespaces = namespaces;
+ }
+
+ public void setCndFiles(List<String> cndFiles) {
+ this.cndFiles = cndFiles;
+ }
+
+ public void setBundleContext(BundleContext bundleContext) {
+ this.bundleContext = bundleContext;
+ }
+
+ protected BundleContext getBundleContext() {
+ return bundleContext;
+ }
+
+ public void setForceCndImport(Boolean forceCndUpdate) {
+ this.forceCndImport = forceCndUpdate;
+ }
+
+ public void setResourceLoader(ResourceLoader resourceLoader) {
+ this.resourceLoader = resourceLoader;
+ }
+
+ public void setAdminCredentials(Credentials adminCredentials) {
+ this.adminCredentials = adminCredentials;
+ }
+
+}
--- /dev/null
+/*
+ * Copyright (C) 2007-2012 Argeo GmbH
+ *
+ * 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 org.argeo.jackrabbit;
+
+import java.util.Hashtable;
+import java.util.Properties;
+
+import javax.jcr.Repository;
+
+import org.osgi.framework.BundleContext;
+
+/**
+ * OSGi-aware Jackrabbit repository factory which can retrieve/publish
+ * {@link Repository} as OSGi services.
+ */
+public class OsgiJackrabbitRepositoryFactory extends
+ JackrabbitRepositoryFactory {
+ private BundleContext bundleContext;
+
+ protected void publish(String alias, Repository repository,
+ Properties properties) {
+ if (bundleContext != null) {
+ // do not modify reference
+ Hashtable<String, String> props = new Hashtable<String, String>();
+ props.putAll(props);
+ props.put(JCR_REPOSITORY_ALIAS, alias);
+ bundleContext.registerService(Repository.class.getName(),
+ repository, props);
+ }
+ }
+
+ public void setBundleContext(BundleContext bundleContext) {
+ this.bundleContext = bundleContext;
+ }
+
+}
--- /dev/null
+<?xml version="1.0"?>
+<!DOCTYPE Repository PUBLIC "-//The Apache Software Foundation//DTD Jackrabbit 1.6//EN"
+ "http://jackrabbit.apache.org/dtd/repository-2.0.dtd">
+<Repository>
+ <!-- File system and datastore -->
+ <FileSystem class="org.apache.jackrabbit.core.fs.local.LocalFileSystem">
+ <param name="path" value="${rep.home}/fs" />
+ </FileSystem>
+ <DataStore class="org.apache.jackrabbit.core.data.FileDataStore">
+ <param name="path" value="${rep.home}/datastore" />
+ </DataStore>
+
+ <!-- Workspace templates -->
+ <Workspaces rootPath="${rep.home}/workspaces"
+ defaultWorkspace="${argeo.node.repo.defaultWorkspace}" />
+ <Workspace name="${wsp.name}">
+ <FileSystem class="org.apache.jackrabbit.core.fs.local.LocalFileSystem">
+ <param name="path" value="${wsp.home}" />
+ </FileSystem>
+ <PersistenceManager
+ class="org.apache.jackrabbit.core.persistence.bundle.BundleFsPersistenceManager" />
+ <SearchIndex class="org.apache.jackrabbit.core.query.lucene.SearchIndex">
+ <param name="path" value="${wsp.home}/index" />
+ </SearchIndex>
+ </Workspace>
+
+ <!-- Versioning -->
+ <Versioning rootPath="${rep.home}/version">
+ <FileSystem class="org.apache.jackrabbit.core.fs.local.LocalFileSystem">
+ <param name="path" value="${rep.home}/version" />
+ </FileSystem>
+ <PersistenceManager
+ class="org.apache.jackrabbit.core.persistence.bundle.BundleFsPersistenceManager" />
+ </Versioning>
+
+ <!-- Indexing -->
+ <SearchIndex class="org.apache.jackrabbit.core.query.lucene.SearchIndex">
+ <param name="path" value="${rep.home}/index" />
+ <param name="extractorPoolSize" value="2" />
+ <param name="supportHighlighting" value="true" />
+ </SearchIndex>
+
+ <!-- Security -->
+ <Security appName="Jackrabbit">
+ <SecurityManager class="org.argeo.security.jackrabbit.ArgeoSecurityManager"
+ workspaceName="security">
+ </SecurityManager>
+ <AccessManager class="org.argeo.security.jackrabbit.ArgeoAccessManager">
+ </AccessManager>
+ <LoginModule class="org.argeo.security.jackrabbit.ArgeoLoginModule">
+ </LoginModule>
+ </Security>
+</Repository>
\ No newline at end of file
--- /dev/null
+<?xml version="1.0"?>
+<!DOCTYPE Repository PUBLIC "-//The Apache Software Foundation//DTD Jackrabbit 1.6//EN"
+ "http://jackrabbit.apache.org/dtd/repository-2.0.dtd">
+<Repository>
+ <!-- Shared datasource -->
+ <DataSources>
+ <DataSource name="dataSource">
+ <param name="driver" value="org.h2.Driver" />
+ <param name="url" value="jdbc:h2:${rep.home}/h2/repository" />
+ <param name="user" value="sa" />
+ <param name="password" value="" />
+ <param name="databaseType" value="h2" />
+ <param name="maxPoolSize" value="10" />
+ </DataSource>
+ </DataSources>
+
+ <!-- File system and datastore -->
+ <FileSystem class="org.apache.jackrabbit.core.fs.db.DbFileSystem">
+ <param name="dataSourceName" value="dataSource" />
+ <param name="schema" value="default" />
+ <param name="schemaObjectPrefix" value="fs_" />
+ </FileSystem>
+ <DataStore class="org.apache.jackrabbit.core.data.FileDataStore">
+ <param name="path" value="${rep.home}/datastore" />
+ </DataStore>
+
+ <!-- Workspace templates -->
+ <Workspaces rootPath="${rep.home}/workspaces"
+ defaultWorkspace="main" />
+ <Workspace name="${wsp.name}">
+ <FileSystem class="org.apache.jackrabbit.core.fs.db.DbFileSystem">
+ <param name="dataSourceName" value="dataSource" />
+ <param name="schema" value="default" />
+ <param name="schemaObjectPrefix" value="${wsp.name}_fs_" />
+ </FileSystem>
+ <PersistenceManager
+ class="org.apache.jackrabbit.core.persistence.pool.H2PersistenceManager">
+ <param name="dataSourceName" value="dataSource" />
+ <param name="schemaObjectPrefix" value="${wsp.name}_pm_" />
+ </PersistenceManager>
+ <SearchIndex class="org.apache.jackrabbit.core.query.lucene.SearchIndex">
+ <param name="path" value="${wsp.home}/index" />
+ </SearchIndex>
+ </Workspace>
+
+ <!-- Versioning -->
+ <Versioning rootPath="${rep.home}/version">
+ <FileSystem class="org.apache.jackrabbit.core.fs.db.DbFileSystem">
+ <param name="dataSourceName" value="dataSource" />
+ <param name="schema" value="default" />
+ <param name="schemaObjectPrefix" value="fs_ver_" />
+ </FileSystem>
+ <PersistenceManager
+ class="org.apache.jackrabbit.core.persistence.pool.H2PersistenceManager">
+ <param name="dataSourceName" value="dataSource" />
+ <param name="schemaObjectPrefix" value="pm_ver_" />
+ </PersistenceManager>
+ </Versioning>
+
+ <!-- Indexing -->
+ <SearchIndex class="org.apache.jackrabbit.core.query.lucene.SearchIndex">
+ <param name="path" value="${rep.home}/index" />
+ <param name="extractorPoolSize" value="2" />
+ <param name="supportHighlighting" value="true" />
+ </SearchIndex>
+
+ <!-- Security -->
+ <Security appName="Jackrabbit">
+ <SecurityManager class="org.argeo.security.jackrabbit.ArgeoSecurityManager"
+ workspaceName="security">
+ </SecurityManager>
+ <AccessManager class="org.argeo.security.jackrabbit.ArgeoAccessManager">
+ </AccessManager>
+ <LoginModule class="org.argeo.security.jackrabbit.ArgeoLoginModule">
+ </LoginModule>
+ </Security>
+</Repository>
\ No newline at end of file
--- /dev/null
+<?xml version="1.0"?>
+<!--
+
+ Copyright (C) 2007-2012 Argeo GmbH
+
+ 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.
+
+-->
+<!DOCTYPE Repository PUBLIC "-//The Apache Software Foundation//DTD Jackrabbit 1.6//EN"
+ "http://jackrabbit.apache.org/dtd/repository-2.0.dtd">
+<Repository>
+ <!-- File system and datastore -->
+ <FileSystem class="org.apache.jackrabbit.core.fs.mem.MemoryFileSystem" />
+
+ <!-- Workspace templates -->
+ <Workspaces rootPath="${rep.home}/workspaces"
+ defaultWorkspace="main" configRootPath="/workspaces" />
+ <Workspace name="${wsp.name}">
+ <FileSystem class="org.apache.jackrabbit.core.fs.mem.MemoryFileSystem" />
+ <PersistenceManager
+ class="org.apache.jackrabbit.core.persistence.bundle.BundleFsPersistenceManager">
+ <param name="blobFSBlockSize" value="1" />
+ </PersistenceManager>
+ <SearchIndex class="org.apache.jackrabbit.core.query.lucene.SearchIndex">
+ <param name="path" value="${rep.home}/repository/index" />
+ <param name="directoryManagerClass"
+ value="org.apache.jackrabbit.core.query.lucene.directory.RAMDirectoryManager" />
+ <FileSystem class="org.apache.jackrabbit.core.fs.mem.MemoryFileSystem" />
+ </SearchIndex>
+ </Workspace>
+
+ <!-- Versioning -->
+ <Versioning rootPath="${rep.home}/version">
+ <FileSystem class="org.apache.jackrabbit.core.fs.mem.MemoryFileSystem" />
+ <PersistenceManager
+ class="org.apache.jackrabbit.core.persistence.bundle.BundleFsPersistenceManager">
+ <param name="blobFSBlockSize" value="1" />
+ </PersistenceManager>
+ </Versioning>
+
+ <!-- Indexing -->
+ <SearchIndex class="org.apache.jackrabbit.core.query.lucene.SearchIndex">
+ <param name="path" value="${rep.home}/repository/index" />
+ <param name="directoryManagerClass"
+ value="org.apache.jackrabbit.core.query.lucene.directory.RAMDirectoryManager" />
+ <FileSystem class="org.apache.jackrabbit.core.fs.mem.MemoryFileSystem" />
+ </SearchIndex>
+
+ <!-- Security -->
+ <Security appName="Jackrabbit">
+ <SecurityManager class="org.argeo.security.jackrabbit.ArgeoSecurityManager"
+ workspaceName="security">
+ </SecurityManager>
+ <AccessManager class="org.argeo.security.jackrabbit.ArgeoAccessManager">
+ </AccessManager>
+ <LoginModule class="org.argeo.security.jackrabbit.ArgeoLoginModule">
+ </LoginModule>
+ </Security>
+</Repository>
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (C) 2007-2012 Argeo GmbH
+ *
+ * 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 org.argeo.jackrabbit.servlet;
+
+import java.io.Serializable;
+
+import javax.jcr.LoginException;
+import javax.jcr.Repository;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.jackrabbit.server.SessionProvider;
+import org.argeo.jcr.JcrUtils;
+
+/**
+ * Implements an open session in view patter: a new JCR session is created for
+ * each request
+ */
+public class OpenInViewSessionProvider implements SessionProvider, Serializable {
+ private static final long serialVersionUID = 2270957712453841368L;
+
+ private final static Log log = LogFactory
+ .getLog(OpenInViewSessionProvider.class);
+
+ public Session getSession(HttpServletRequest request, Repository rep,
+ String workspace) throws LoginException, ServletException,
+ RepositoryException {
+ return login(request, rep, workspace);
+ }
+
+ protected Session login(HttpServletRequest request, Repository repository,
+ String workspace) throws RepositoryException {
+ if (log.isTraceEnabled())
+ log.trace("Login to workspace "
+ + (workspace == null ? "<default>" : workspace)
+ + " in web session " + request.getSession().getId());
+ return repository.login(workspace);
+ }
+
+ public void releaseSession(Session session) {
+ JcrUtils.logoutQuietly(session);
+ if (log.isTraceEnabled())
+ log.trace("Logged out remote JCR session " + session);
+ }
+
+ public void init() {
+ }
+
+ public void destroy() {
+ }
+
+}
--- /dev/null
+/*
+ * Copyright (C) 2007-2012 Argeo GmbH
+ *
+ * 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 org.argeo.jackrabbit.servlet;
+
+import javax.jcr.Repository;
+
+import org.apache.jackrabbit.server.SessionProvider;
+import org.apache.jackrabbit.server.remoting.davex.JcrRemotingServlet;
+
+/** Provides remote access to a JCR repository */
+public class RemotingServlet extends JcrRemotingServlet {
+ public final static String INIT_PARAM_RESOURCE_PATH_PREFIX = JcrRemotingServlet.INIT_PARAM_RESOURCE_PATH_PREFIX;
+ public final static String INIT_PARAM_HOME = JcrRemotingServlet.INIT_PARAM_HOME;
+ public final static String INIT_PARAM_TMP_DIRECTORY = JcrRemotingServlet.INIT_PARAM_TMP_DIRECTORY;
+
+ private static final long serialVersionUID = 3131835511468341309L;
+
+ private final Repository repository;
+ private final SessionProvider sessionProvider;
+
+ public RemotingServlet(Repository repository,
+ SessionProvider sessionProvider) {
+ this.repository = repository;
+ this.sessionProvider = sessionProvider;
+ }
+
+ @Override
+ protected Repository getRepository() {
+ return repository;
+ }
+
+ @Override
+ protected SessionProvider getSessionProvider() {
+ return sessionProvider;
+ }
+
+}
--- /dev/null
+/*
+ * Copyright (C) 2007-2012 Argeo GmbH
+ *
+ * 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 org.argeo.jackrabbit.servlet;
+
+import java.io.IOException;
+
+import javax.jcr.Repository;
+import javax.servlet.ServletException;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.jackrabbit.server.SessionProvider;
+import org.apache.jackrabbit.webdav.DavException;
+import org.apache.jackrabbit.webdav.DavResource;
+import org.apache.jackrabbit.webdav.WebdavRequest;
+import org.apache.jackrabbit.webdav.WebdavResponse;
+import org.apache.jackrabbit.webdav.simple.SimpleWebdavServlet;
+
+/** WebDav servlet whose repository is injected */
+public class WebdavServlet extends SimpleWebdavServlet {
+ public final static String INIT_PARAM_RESOURCE_CONFIG = SimpleWebdavServlet.INIT_PARAM_RESOURCE_CONFIG;
+ public final static String INIT_PARAM_RESOURCE_PATH_PREFIX = SimpleWebdavServlet.INIT_PARAM_RESOURCE_PATH_PREFIX;
+
+ private static final long serialVersionUID = -369787931175177080L;
+
+ private final static Log log = LogFactory.getLog(WebdavServlet.class);
+
+ private final Repository repository;
+
+ public WebdavServlet(Repository repository, SessionProvider sessionProvider) {
+ this.repository = repository;
+ setSessionProvider(sessionProvider);
+ }
+
+ public Repository getRepository() {
+ return repository;
+ }
+
+ @Override
+ protected boolean execute(WebdavRequest request, WebdavResponse response,
+ int method, DavResource resource) throws ServletException,
+ IOException, DavException {
+ if (log.isTraceEnabled())
+ log.trace(request.getMethod() + "\t" + request.getPathInfo());
+ boolean res = super.execute(request, response, method, resource);
+ return res;
+ }
+
+}
<relativePath>..</relativePath>
</parent>
<artifactId>org.argeo.util</artifactId>
- <name>Commons Util (no third party dependencies)</name>
- <!-- <dependencies> -->
- <!-- <dependency> -->
- <!-- <groupId>org.argeo.tp</groupId> -->
- <!-- <artifactId>junit</artifactId> -->
- <!-- <scope>test</scope> -->
- <!-- </dependency> -->
- <!-- </dependencies> -->
-<!-- <dependencies> -->
-<!-- <dependency> -->
-<!-- <groupId>org.argeo.tp</groupId> -->
-<!-- <artifactId>argeo-tp</artifactId> -->
-<!-- <version>${version.argeo-distribution}</version> -->
-<!-- <type>pom</type> -->
-<!-- <scope>provided</scope> -->
-<!-- </dependency> -->
-<!-- </dependencies> -->
+ <name>Commons Utilities</name>
</project>
\ No newline at end of file
<!-- Security -->
<module>org.argeo.security.core</module>
<module>org.argeo.security.jackrabbit</module>
- <module>org.argeo.security.ldap</module>
- <!-- Security Services -->
- <!-- <module>org.argeo.security.auth.ldap</module> -->
- <!-- <module>org.argeo.security.dao.ldap</module> -->
- <!-- <module>org.argeo.security.dao.cli</module> -->
- <!-- <module>org.argeo.security.dao.jackrabbit</module> -->
- <!-- Server -->
- <module>org.argeo.server.core</module>
- <!-- <module>org.argeo.server.jcr.mvc</module> -->
- <module>org.argeo.server.jackrabbit</module>
<!-- Eclipse -->
<module>org.argeo.eclipse.ui</module>
<module>org.argeo.eclipse.ui.rap</module>
- <!-- Argeo Node -->
- <!-- <module>org.argeo.node.repo.jackrabbit</module> -->
+ <!-- CMS -->
<module>org.argeo.cms</module>
<!-- Workbench -->
<module>org.argeo.eclipse.ui.workbench</module>
<module>org.argeo.eclipse.ui.workbench.rap</module>
- <!-- <module>org.argeo.security.equinox</module> -->
<module>org.argeo.security.ui</module>
<module>org.argeo.security.ui.admin</module>
<module>org.argeo.security.ui.rap</module>
- <!-- Dependencies and Distributions -->
+ <!-- Distribution -->
<module>dep</module>
<module>dist</module>
</modules>