From: Mathieu Baudier Date: Wed, 14 Sep 2016 14:16:57 +0000 (+0000) Subject: Rename Commons bundles X-Git-Tag: argeo-commons-2.1.46~37 X-Git-Url: https://git.argeo.org/?a=commitdiff_plain;h=e66b9893b0e511f8ab295e3cee42b7dc966f1597;hp=8260f4470f514ea347ca53f5b4dfc632c4a4de66;p=lgpl%2Fargeo-commons.git Rename Commons bundles git-svn-id: https://svn.argeo.org/commons/trunk@9138 4cfe0d0a-d680-48aa-b62c-e0a02a3f76cc --- diff --git a/dep/org.argeo.dep.cms.client/pom.xml b/dep/org.argeo.dep.cms.client/pom.xml index da2dc1160..ca8f8adbb 100644 --- a/dep/org.argeo.dep.cms.client/pom.xml +++ b/dep/org.argeo.dep.cms.client/pom.xml @@ -19,7 +19,7 @@ org.argeo.commons - org.argeo.server.jcr + org.argeo.jcr 2.1.46-SNAPSHOT diff --git a/dep/org.argeo.dep.cms.node/pom.xml b/dep/org.argeo.dep.cms.node/pom.xml index 1cf3918b3..1c0808965 100644 --- a/dep/org.argeo.dep.cms.node/pom.xml +++ b/dep/org.argeo.dep.cms.node/pom.xml @@ -19,7 +19,7 @@ org.argeo.commons - org.argeo.cms.api + org.argeo.node.api 2.1.46-SNAPSHOT @@ -29,12 +29,12 @@ org.argeo.commons - org.argeo.security.core + org.argeo.enterprise 2.1.46-SNAPSHOT org.argeo.commons - org.argeo.security.jackrabbit + org.argeo.ext.jackrabbit 2.1.46-SNAPSHOT diff --git a/dep/org.argeo.dep.cms.platform/pom.xml b/dep/org.argeo.dep.cms.platform/pom.xml index b450008ed..91a354198 100644 --- a/dep/org.argeo.dep.cms.platform/pom.xml +++ b/dep/org.argeo.dep.cms.platform/pom.xml @@ -17,11 +17,6 @@ org.argeo.dep.cms.node 2.1.46-SNAPSHOT - - org.argeo.commons - org.argeo.security.core - 2.1.46-SNAPSHOT - org.argeo.commons org.argeo.security.ui @@ -34,17 +29,17 @@ org.argeo.commons - org.argeo.security.ui.admin + org.argeo.cms.ui.workbench 2.1.46-SNAPSHOT org.argeo.commons - org.argeo.eclipse.ui.workbench.rap + org.argeo.ext.rap.ui.workbench 2.1.46-SNAPSHOT org.argeo.commons - org.argeo.security.ui.rap + org.argeo.cms.ui.workbench 2.1.46-SNAPSHOT diff --git a/org.argeo.cms.api/.classpath b/org.argeo.cms.api/.classpath deleted file mode 100644 index eca7bdba8..000000000 --- a/org.argeo.cms.api/.classpath +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - diff --git a/org.argeo.cms.api/.project b/org.argeo.cms.api/.project deleted file mode 100644 index 346a99ff4..000000000 --- a/org.argeo.cms.api/.project +++ /dev/null @@ -1,28 +0,0 @@ - - - org.argeo.cms.api - - - - - - org.eclipse.jdt.core.javabuilder - - - - - org.eclipse.pde.ManifestBuilder - - - - - org.eclipse.pde.SchemaBuilder - - - - - - org.eclipse.pde.PluginNature - org.eclipse.jdt.core.javanature - - diff --git a/org.argeo.cms.api/bnd.bnd b/org.argeo.cms.api/bnd.bnd deleted file mode 100644 index 52a2bdd57..000000000 --- a/org.argeo.cms.api/bnd.bnd +++ /dev/null @@ -1 +0,0 @@ -Provide-Capability: cms.datamodel;name=node;cnd=/org/argeo/node/node.cnd \ No newline at end of file diff --git a/org.argeo.cms.api/build.properties b/org.argeo.cms.api/build.properties deleted file mode 100644 index 34d2e4d2d..000000000 --- a/org.argeo.cms.api/build.properties +++ /dev/null @@ -1,4 +0,0 @@ -source.. = src/ -output.. = bin/ -bin.includes = META-INF/,\ - . diff --git a/org.argeo.cms.api/pom.xml b/org.argeo.cms.api/pom.xml deleted file mode 100644 index 12911e680..000000000 --- a/org.argeo.cms.api/pom.xml +++ /dev/null @@ -1,15 +0,0 @@ - - - 4.0.0 - - org.argeo.commons - argeo-commons - 2.1.46-SNAPSHOT - .. - - org.argeo.cms.api - Commons CMS API - jar - - - \ No newline at end of file diff --git a/org.argeo.cms.api/src/org/argeo/node/ArgeoLogListener.java b/org.argeo.cms.api/src/org/argeo/node/ArgeoLogListener.java deleted file mode 100644 index 698dfe1be..000000000 --- a/org.argeo.cms.api/src/org/argeo/node/ArgeoLogListener.java +++ /dev/null @@ -1,39 +0,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. - */ -package org.argeo.node; - -/** Framework agnostic interface for log notifications */ -public interface ArgeoLogListener { - /** - * Appends a log - * - * @param username - * authentified user, null for anonymous - * @param level - * INFO, DEBUG, WARN, etc. (logging framework specific) - * @param category - * hierarchy (logging framework specific) - * @param thread - * name of the thread which logged this message - * @param msg - * any object as long as its toString() method returns the - * message - * @param the - * exception in log4j ThrowableStrRep format - */ - public void appendLog(String username, Long timestamp, String level, - String category, String thread, Object msg, String[] exception); -} diff --git a/org.argeo.cms.api/src/org/argeo/node/ArgeoLogger.java b/org.argeo.cms.api/src/org/argeo/node/ArgeoLogger.java deleted file mode 100644 index 213286d44..000000000 --- a/org.argeo.cms.api/src/org/argeo/node/ArgeoLogger.java +++ /dev/null @@ -1,46 +0,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. - */ -package org.argeo.node; - -/** - * Logging framework agnostic identifying a logging service, to which one can - * register - */ -public interface ArgeoLogger { - /** - * Register for events by threads with the same authentication (or all - * threads if admin) - */ - public void register(ArgeoLogListener listener, - Integer numberOfPreviousEvents); - - /** - * For admin use only: register for all users - * - * @param listener - * the log listener - * @param numberOfPreviousEvents - * the number of previous events to notify - * @param everything - * if true even anonymous is logged - */ - public void registerForAll(ArgeoLogListener listener, - Integer numberOfPreviousEvents, boolean everything); - - public void unregister(ArgeoLogListener listener); - - public void unregisterForAll(ArgeoLogListener listener); -} diff --git a/org.argeo.cms.api/src/org/argeo/node/DataAdminPrincipal.java b/org.argeo.cms.api/src/org/argeo/node/DataAdminPrincipal.java deleted file mode 100644 index 743c96f2e..000000000 --- a/org.argeo.cms.api/src/org/argeo/node/DataAdminPrincipal.java +++ /dev/null @@ -1,30 +0,0 @@ -package org.argeo.node; - -import java.security.Principal; - -/** Allows to modify any data. */ -public final class DataAdminPrincipal implements Principal { - // FIXME put auth constants in API - private final String name = "OU=node"; - - @Override - public String getName() { - return name; - } - - @Override - public int hashCode() { - return name.hashCode(); - } - - @Override - public boolean equals(Object obj) { - return this == obj; - } - - @Override - public String toString() { - return name.toString(); - } - -} diff --git a/org.argeo.cms.api/src/org/argeo/node/DataModelNamespace.java b/org.argeo.cms.api/src/org/argeo/node/DataModelNamespace.java deleted file mode 100644 index 578419609..000000000 --- a/org.argeo.cms.api/src/org/argeo/node/DataModelNamespace.java +++ /dev/null @@ -1,16 +0,0 @@ -package org.argeo.node; - -import org.osgi.resource.Namespace; - -/** CMS Data Model capability namespace. */ -public class DataModelNamespace extends Namespace { - - public static final String CMS_DATA_MODEL_NAMESPACE = "cms.datamodel"; - public static final String CAPABILITY_NAME_ATTRIBUTE = "name"; - public static final String CAPABILITY_CND_ATTRIBUTE = "cnd"; - - private DataModelNamespace() { - // empty - } - -} diff --git a/org.argeo.cms.api/src/org/argeo/node/EnumAD.java b/org.argeo.cms.api/src/org/argeo/node/EnumAD.java deleted file mode 100644 index 1ee6d39f0..000000000 --- a/org.argeo.cms.api/src/org/argeo/node/EnumAD.java +++ /dev/null @@ -1,59 +0,0 @@ -package org.argeo.node; - -import org.osgi.service.metatype.AttributeDefinition; - -interface EnumAD extends AttributeDefinition { - String name(); - - default Object getDefault() { - return null; - } - - @Override - default String getName() { - return name(); - } - - @Override - default String getID() { - return getClass().getName() + "." + name(); - } - - @Override - default String getDescription() { - return null; - } - - @Override - default int getCardinality() { - return 0; - } - - @Override - default int getType() { - return STRING; - } - - @Override - default String[] getOptionValues() { - return null; - } - - @Override - default String[] getOptionLabels() { - return null; - } - - @Override - default String validate(String value) { - return null; - } - - @Override - default String[] getDefaultValue() { - Object value = getDefault(); - if (value == null) - return null; - return new String[] { value.toString() }; - } -} diff --git a/org.argeo.cms.api/src/org/argeo/node/EnumOCD.java b/org.argeo.cms.api/src/org/argeo/node/EnumOCD.java deleted file mode 100644 index daa2f540d..000000000 --- a/org.argeo.cms.api/src/org/argeo/node/EnumOCD.java +++ /dev/null @@ -1,50 +0,0 @@ -package org.argeo.node; - -import java.io.IOException; -import java.io.InputStream; -import java.util.ArrayList; -import java.util.EnumSet; -import java.util.List; - -import org.osgi.service.metatype.AttributeDefinition; -import org.osgi.service.metatype.ObjectClassDefinition; - -class EnumOCD> implements ObjectClassDefinition { - private final Class enumClass; - private String locale; - - public EnumOCD(Class clazz, String locale) { - this.enumClass = clazz; - this.locale = locale; - } - - @Override - public String getName() { - return null; - } - - @Override - public String getID() { - return enumClass.getName(); - } - - @Override - public String getDescription() { - return null; - } - - @Override - public AttributeDefinition[] getAttributeDefinitions(int filter) { - EnumSet set = EnumSet.allOf(enumClass); - List attrs = new ArrayList<>(); - for (T key : set) - attrs.add((AttributeDefinition) key); - return attrs.toArray(new AttributeDefinition[attrs.size()]); - } - - @Override - public InputStream getIcon(int size) throws IOException { - return null; - } - -} diff --git a/org.argeo.cms.api/src/org/argeo/node/NodeConstants.java b/org.argeo.cms.api/src/org/argeo/node/NodeConstants.java deleted file mode 100644 index 477b0b96b..000000000 --- a/org.argeo.cms.api/src/org/argeo/node/NodeConstants.java +++ /dev/null @@ -1,50 +0,0 @@ -package org.argeo.node; - -public interface NodeConstants { - /* - * PIDs - */ - String NODE_STATE_PID = "org.argeo.node.state"; - String NODE_DEPLOYMENT_PID = "org.argeo.node.deployment"; - String NODE_INSTANCE_PID = "org.argeo.node.instance"; - -// String NODE_REPO_PID = "org.argeo.node.repo"; - String NODE_USER_ADMIN_PID = "org.argeo.node.userAdmin"; - - /* - * FACTORY PIDs - */ - String NODE_REPOS_FACTORY_PID = "org.argeo.node.repos"; - String NODE_USER_DIRECTORIES_FACTORY_PID = "org.argeo.node.userDirectories"; - - /* - * DEPLOY - */ - String DEPLOY_BASEDN = "ou=deploy,ou=node"; - - /* - * FRAMEWORK PROPERTIES - */ - String NODE_INIT = "argeo.node.init"; - String I18N_DEFAULT_LOCALE = "argeo.i18n.defaultLocale"; - String I18N_LOCALES = "argeo.i18n.locales"; - // Node Security - String ROLES_URI = "argeo.node.roles.uri"; - /** URI to an LDIF file or LDAP server used as initialization or backend */ - String USERADMIN_URIS = "argeo.node.useradmin.uris"; - // Node - /** Properties configuring the node repository */ - String NODE_REPO_PROP_PREFIX = "argeo.node.repo."; - - /* - * STANDARD ATTRIBUTES - */ - String CN = "cn"; - String OU = "ou"; - String LABELED_URI = "labeledUri"; - - /* - * STANDARD VALUES - */ - String DEFAULT = "default"; -} diff --git a/org.argeo.cms.api/src/org/argeo/node/NodeDeployment.java b/org.argeo.cms.api/src/org/argeo/node/NodeDeployment.java deleted file mode 100644 index 8e5558d60..000000000 --- a/org.argeo.cms.api/src/org/argeo/node/NodeDeployment.java +++ /dev/null @@ -1,5 +0,0 @@ -package org.argeo.node; - -public interface NodeDeployment { - Long getAvailableSince(); -} diff --git a/org.argeo.cms.api/src/org/argeo/node/NodeInstance.java b/org.argeo.cms.api/src/org/argeo/node/NodeInstance.java deleted file mode 100644 index e1e4bcdfa..000000000 --- a/org.argeo.cms.api/src/org/argeo/node/NodeInstance.java +++ /dev/null @@ -1,5 +0,0 @@ -package org.argeo.node; - -public interface NodeInstance { - -} diff --git a/org.argeo.cms.api/src/org/argeo/node/NodeOID.java b/org.argeo.cms.api/src/org/argeo/node/NodeOID.java deleted file mode 100644 index 9d8ff3d3a..000000000 --- a/org.argeo.cms.api/src/org/argeo/node/NodeOID.java +++ /dev/null @@ -1,18 +0,0 @@ -package org.argeo.node; - -interface NodeOID { - String BASE = "1.3.6.1.4.1" + ".48308" + ".1"; - - // ATTRIBUTE TYPES - String ATTRIBUTE_TYPES = BASE + ".4"; - String URI = ATTRIBUTE_TYPES + ".1"; - String HTTP_PORT = ATTRIBUTE_TYPES + ".2"; - String HTTPS_PORT = ATTRIBUTE_TYPES + ".3"; - - // OBJECT CLASSES - String OBJECT_CLASSES = BASE + ".6"; - String JCR_REPOSITORY = OBJECT_CLASSES + ".1"; - - // EXTERNAL - String LABELED_URI = "1.3.6.1.4.1.250.1.57"; -} diff --git a/org.argeo.cms.api/src/org/argeo/node/NodeState.java b/org.argeo.cms.api/src/org/argeo/node/NodeState.java deleted file mode 100644 index d7148c68f..000000000 --- a/org.argeo.cms.api/src/org/argeo/node/NodeState.java +++ /dev/null @@ -1,17 +0,0 @@ -package org.argeo.node; - -import java.util.List; -import java.util.Locale; - -public interface NodeState { - Locale getDefaultLocale(); - - List getLocales(); - - String getHostname(); - - boolean isClean(); - - Long getAvailableSince(); - -} diff --git a/org.argeo.cms.api/src/org/argeo/node/RepoConf.java b/org.argeo.cms.api/src/org/argeo/node/RepoConf.java deleted file mode 100644 index be4f6f7f7..000000000 --- a/org.argeo.cms.api/src/org/argeo/node/RepoConf.java +++ /dev/null @@ -1,66 +0,0 @@ -package org.argeo.node; - -/** JCR repository configuration */ -public enum RepoConf implements EnumAD { - /** Repository type */ - type("localfs"), - /** Default workspace */ - @Deprecated - defaultWorkspace("main"), - /** Database URL */ - dburl(null), - /** Database user */ - dbuser(null), - /** Database password */ - dbpassword(null), - - /** The identifier (can be an URL locating the repo) */ - labeledUri(null), - // - // JACKRABBIT SPECIFIC - // - /** Maximum database pool size */ - maxPoolSize(10), - /** Maximum cache size in MB */ - @Deprecated - maxCacheMB(null), - /** Bundle cache size in MB */ - bundleCacheMB(8), - /** Extractor pool size */ - extractorPoolSize(0), - /** Search cache size */ - searchCacheSize(1000), - /** Max volatile index size */ - maxVolatileIndexSize(1048576); - - /** The default value. */ - private Object def; - private String oid; - - RepoConf(String oid, Object def) { - this.oid = oid; - this.def = def; - } - - RepoConf(Object def) { - this.def = def; - } - - public Object getDefault() { - return def; - } - - @Override - public String getID() { - if (oid != null) - return oid; - return EnumAD.super.getID(); - } - - public static class OCD extends EnumOCD { - public OCD(String locale) { - super(RepoConf.class, locale); - } - } - -} diff --git a/org.argeo.cms.api/src/org/argeo/node/node.cnd b/org.argeo.cms.api/src/org/argeo/node/node.cnd deleted file mode 100644 index fbfea9dd9..000000000 --- a/org.argeo.cms.api/src/org/argeo/node/node.cnd +++ /dev/null @@ -1,72 +0,0 @@ - - -// GENERIC TYPES NOT AVAILABLE IN JCR -[argeo:link] > mix:created, mix:lastModified -mixin -// URI(s) -- argeo:uri (STRING) m - -[argeo:references] > nt:unstructured -- * (REFERENCE) * - -// DATA MODEL -[argeo:dataModel] > mix:created, mix:lastModified, mix:versionable -mixin -- argeo:uri (STRING) m -- argeo:dataModelVersion (STRING) m - -// USER NODES -// user should be lower case, between 3 and 15 characters long -[argeo:userHome] > mix:created, mix:lastModified -mixin -- argeo:userID (STRING) m -- argeo:remoteRoles (STRING) * -// deprecated. for backward compatibility: -+ argeo:profile (argeo:userProfile) -+ argeo:keyring (argeo:pbeSpec) -+ argeo:preferences (argeo:preferenceNode) - -[argeo:userProfile] > mix:created, mix:lastModified, mix:title, mix:versionable -mixin -- argeo:userID (STRING) m -- argeo:enabled (BOOLEAN) -- argeo:accountNonExpired (BOOLEAN) -- argeo:accountNonLocked (BOOLEAN) -- argeo:credentialsNonExpired (BOOLEAN) - -[argeo:preferenceNode] > mix:lastModified, mix:versionable -mixin -+ * (argeo:preferenceNode) * version - -[argeo:remoteRepository] > nt:unstructured -- argeo:uri (STRING) -- argeo:userID (STRING) -+ argeo:password (argeo:encrypted) - -// TABULAR CONTENT -[argeo:table] > nt:file -+ * (argeo:column) * - -[argeo:column] > mix:title -- jcr:requiredType (STRING) = 'STRING' - -[argeo:csv] > nt:resource - -// CRYPTO -[argeo:encrypted] > nt:base -mixin -// initialization vector used by some algorithms -- argeo:iv (BINARY) - -[argeo:pbeKeySpec] > nt:base -mixin -- argeo:secretKeyFactory (STRING) -- argeo:salt (BINARY) -- argeo:iterationCount (LONG) -- argeo:keyLength (LONG) -- argeo:secretKeyEncryption (STRING) - -[argeo:pbeSpec] > argeo:pbeKeySpec -mixin -- argeo:cipher (STRING) - diff --git a/org.argeo.cms.api/src/org/argeo/node/package-info.java b/org.argeo.cms.api/src/org/argeo/node/package-info.java deleted file mode 100644 index fda3baec5..000000000 --- a/org.argeo.cms.api/src/org/argeo/node/package-info.java +++ /dev/null @@ -1,5 +0,0 @@ -/** - * Abstractions or constants related to an Argeo Node, an active repository of - * linked data. - */ -package org.argeo.node; \ No newline at end of file diff --git a/org.argeo.cms.api/src/org/argeo/node/packageinfo b/org.argeo.cms.api/src/org/argeo/node/packageinfo deleted file mode 100644 index 2c9afe82e..000000000 --- a/org.argeo.cms.api/src/org/argeo/node/packageinfo +++ /dev/null @@ -1 +0,0 @@ -version 2.1.0 \ No newline at end of file diff --git a/org.argeo.cms.ui.workbench.rap/.classpath b/org.argeo.cms.ui.workbench.rap/.classpath new file mode 100644 index 000000000..457b11571 --- /dev/null +++ b/org.argeo.cms.ui.workbench.rap/.classpath @@ -0,0 +1,9 @@ + + + + + + + diff --git a/org.argeo.cms.ui.workbench.rap/.project b/org.argeo.cms.ui.workbench.rap/.project new file mode 100644 index 000000000..49b4b902d --- /dev/null +++ b/org.argeo.cms.ui.workbench.rap/.project @@ -0,0 +1,28 @@ + + + org.argeo.cms.ui.workbench.rap + + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.pde.ManifestBuilder + + + + + org.eclipse.pde.SchemaBuilder + + + + + + org.eclipse.pde.PluginNature + org.eclipse.jdt.core.javanature + + diff --git a/org.argeo.cms.ui.workbench.rap/META-INF/spring/commands.xml b/org.argeo.cms.ui.workbench.rap/META-INF/spring/commands.xml new file mode 100644 index 000000000..802d31de6 --- /dev/null +++ b/org.argeo.cms.ui.workbench.rap/META-INF/spring/commands.xml @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + diff --git a/org.argeo.cms.ui.workbench.rap/META-INF/spring/osgi.xml b/org.argeo.cms.ui.workbench.rap/META-INF/spring/osgi.xml new file mode 100644 index 000000000..84e5d7b8b --- /dev/null +++ b/org.argeo.cms.ui.workbench.rap/META-INF/spring/osgi.xml @@ -0,0 +1,13 @@ + + + + + + \ No newline at end of file diff --git a/org.argeo.cms.ui.workbench.rap/OSGI-INF/l10n/bundle.properties b/org.argeo.cms.ui.workbench.rap/OSGI-INF/l10n/bundle.properties new file mode 100644 index 000000000..4dff7af99 --- /dev/null +++ b/org.argeo.cms.ui.workbench.rap/OSGI-INF/l10n/bundle.properties @@ -0,0 +1 @@ +changePassword=Change password \ No newline at end of file diff --git a/org.argeo.cms.ui.workbench.rap/OSGI-INF/l10n/bundle_fr.properties b/org.argeo.cms.ui.workbench.rap/OSGI-INF/l10n/bundle_fr.properties new file mode 100644 index 000000000..158d6faa0 --- /dev/null +++ b/org.argeo.cms.ui.workbench.rap/OSGI-INF/l10n/bundle_fr.properties @@ -0,0 +1 @@ +changePassword=Changer de mot de passe \ No newline at end of file diff --git a/org.argeo.cms.ui.workbench.rap/OSGI-INF/l10n/bundle_ru.properties b/org.argeo.cms.ui.workbench.rap/OSGI-INF/l10n/bundle_ru.properties new file mode 100644 index 000000000..11dd1007c --- /dev/null +++ b/org.argeo.cms.ui.workbench.rap/OSGI-INF/l10n/bundle_ru.properties @@ -0,0 +1 @@ +changePassword=\u0418\u0437\u043C\u0435\u043D\u0438\u0442\u044C \u043F\u0430\u0440\u043E\u043B\u044C diff --git a/org.argeo.cms.ui.workbench.rap/bnd.bnd b/org.argeo.cms.ui.workbench.rap/bnd.bnd new file mode 100644 index 000000000..31a99179c --- /dev/null +++ b/org.argeo.cms.ui.workbench.rap/bnd.bnd @@ -0,0 +1,12 @@ +Bundle-SymbolicName: org.argeo.cms.ui.workbench.rap;singleton:=true +Bundle-Activator: org.argeo.security.ui.rap.SecureRapActivator +Bundle-ActivationPolicy: lazy +Require-Bundle: org.eclipse.rap.ui,org.eclipse.core.runtime + +Import-Package: org.argeo.eclipse.spring,\ +org.argeo.eclipse.ui.specific,\ +org.argeo.eclipse.ui.workbench.commands,\ +org.argeo.cms,\ +org.argeo.cms.auth,\ +org.argeo.security.ui,\ +* diff --git a/org.argeo.cms.ui.workbench.rap/branding/afterLogout.html b/org.argeo.cms.ui.workbench.rap/branding/afterLogout.html new file mode 100644 index 000000000..ae0901bff --- /dev/null +++ b/org.argeo.cms.ui.workbench.rap/branding/afterLogout.html @@ -0,0 +1,18 @@ + + + +
+ + + + +
+ Login... +
+
+ + \ No newline at end of file diff --git a/org.argeo.cms.ui.workbench.rap/branding/empty.html b/org.argeo.cms.ui.workbench.rap/branding/empty.html new file mode 100644 index 000000000..94fe28ac8 --- /dev/null +++ b/org.argeo.cms.ui.workbench.rap/branding/empty.html @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/org.argeo.cms.ui.workbench.rap/branding/favicon.ico b/org.argeo.cms.ui.workbench.rap/branding/favicon.ico new file mode 100644 index 000000000..213cdf73f Binary files /dev/null and b/org.argeo.cms.ui.workbench.rap/branding/favicon.ico differ diff --git a/org.argeo.cms.ui.workbench.rap/branding/login.html b/org.argeo.cms.ui.workbench.rap/branding/login.html new file mode 100644 index 000000000..6de7eb216 --- /dev/null +++ b/org.argeo.cms.ui.workbench.rap/branding/login.html @@ -0,0 +1,18 @@ + + + +
+ + + + +
+ Login... +
+
+ + \ No newline at end of file diff --git a/org.argeo.cms.ui.workbench.rap/branding/public.html b/org.argeo.cms.ui.workbench.rap/branding/public.html new file mode 100644 index 000000000..e50f6e943 --- /dev/null +++ b/org.argeo.cms.ui.workbench.rap/branding/public.html @@ -0,0 +1,18 @@ + + + +
+ + + + +
+ Refresh... +
+
+ + \ No newline at end of file diff --git a/org.argeo.cms.ui.workbench.rap/build.properties b/org.argeo.cms.ui.workbench.rap/build.properties new file mode 100644 index 000000000..ae37429d8 --- /dev/null +++ b/org.argeo.cms.ui.workbench.rap/build.properties @@ -0,0 +1,2 @@ +source.. = src/ +bin.includes = OSGI-INF/ diff --git a/org.argeo.cms.ui.workbench.rap/icons/closeAll.gif b/org.argeo.cms.ui.workbench.rap/icons/closeAll.gif new file mode 100644 index 000000000..28a3785aa Binary files /dev/null and b/org.argeo.cms.ui.workbench.rap/icons/closeAll.gif differ diff --git a/org.argeo.cms.ui.workbench.rap/icons/exit.png b/org.argeo.cms.ui.workbench.rap/icons/exit.png new file mode 100644 index 000000000..cfbf9d15e Binary files /dev/null and b/org.argeo.cms.ui.workbench.rap/icons/exit.png differ diff --git a/org.argeo.cms.ui.workbench.rap/icons/home.gif b/org.argeo.cms.ui.workbench.rap/icons/home.gif new file mode 100644 index 000000000..fd0c66950 Binary files /dev/null and b/org.argeo.cms.ui.workbench.rap/icons/home.gif differ diff --git a/org.argeo.cms.ui.workbench.rap/icons/main.gif b/org.argeo.cms.ui.workbench.rap/icons/main.gif new file mode 100644 index 000000000..90a00147b Binary files /dev/null and b/org.argeo.cms.ui.workbench.rap/icons/main.gif differ diff --git a/org.argeo.cms.ui.workbench.rap/icons/password.gif b/org.argeo.cms.ui.workbench.rap/icons/password.gif new file mode 100644 index 000000000..a6b251fc8 Binary files /dev/null and b/org.argeo.cms.ui.workbench.rap/icons/password.gif differ diff --git a/org.argeo.cms.ui.workbench.rap/icons/preferences.png b/org.argeo.cms.ui.workbench.rap/icons/preferences.png new file mode 100644 index 000000000..aa0dc0be9 Binary files /dev/null and b/org.argeo.cms.ui.workbench.rap/icons/preferences.png differ diff --git a/org.argeo.cms.ui.workbench.rap/plugin.xml b/org.argeo.cms.ui.workbench.rap/plugin.xml new file mode 100644 index 000000000..243a433d0 --- /dev/null +++ b/org.argeo.cms.ui.workbench.rap/plugin.xml @@ -0,0 +1,179 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/org.argeo.cms.ui.workbench.rap/pom.xml b/org.argeo.cms.ui.workbench.rap/pom.xml new file mode 100644 index 000000000..2822051e2 --- /dev/null +++ b/org.argeo.cms.ui.workbench.rap/pom.xml @@ -0,0 +1,35 @@ + + + 4.0.0 + + org.argeo.commons + 2.1.46-SNAPSHOT + argeo-commons + .. + + org.argeo.cms.ui.workbench.rap + CMS Workbench RAP + jar + + + org.argeo.commons + org.argeo.util + 2.1.46-SNAPSHOT + + + org.argeo.commons + org.argeo.security.ui + 2.1.46-SNAPSHOT + + + org.argeo.commons + org.argeo.eclipse.ui.rap + 2.1.46-SNAPSHOT + + + org.argeo.commons + org.argeo.cms + 2.1.46-SNAPSHOT + + + \ No newline at end of file diff --git a/org.argeo.cms.ui.workbench.rap/src/org/argeo/security/ui/rap/AnonymousEntryPoint.java b/org.argeo.cms.ui.workbench.rap/src/org/argeo/security/ui/rap/AnonymousEntryPoint.java new file mode 100644 index 000000000..04b6f0a9d --- /dev/null +++ b/org.argeo.cms.ui.workbench.rap/src/org/argeo/security/ui/rap/AnonymousEntryPoint.java @@ -0,0 +1,112 @@ +/* + * 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.ui.rap; + +import java.security.PrivilegedAction; + +import javax.security.auth.Subject; +import javax.security.auth.login.LoginContext; +import javax.security.auth.login.LoginException; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.argeo.cms.CmsException; +import org.argeo.cms.auth.AuthConstants; +import org.eclipse.rap.rwt.RWT; +import org.eclipse.rap.rwt.application.EntryPoint; +import org.eclipse.swt.widgets.Display; +import org.eclipse.ui.PlatformUI; + +/** + * RAP entry point which authenticates the subject as anonymous, for public + * unauthenticated access. + */ +public class AnonymousEntryPoint implements EntryPoint { + private final static Log log = LogFactory.getLog(AnonymousEntryPoint.class); + + /** + * How many seconds to wait before invalidating the session if the user has + * not yet logged in. + */ + private Integer sessionTimeout = 5 * 60; + + @Override + public int createUI() { + RWT.getRequest().getSession().setMaxInactiveInterval(sessionTimeout); + + // if (log.isDebugEnabled()) + // log.debug("Anonymous THREAD=" + Thread.currentThread().getId() + // + ", sessionStore=" + RWT.getSessionStore().getId()); + + final Display display = PlatformUI.createDisplay(); + Subject subject = new Subject(); + + final LoginContext loginContext; + try { + loginContext = new LoginContext(AuthConstants.LOGIN_CONTEXT_ANONYMOUS, + subject); + loginContext.login(); + } catch (LoginException e1) { + throw new CmsException("Cannot initialize login context", e1); + } + + // identify after successful login + if (log.isDebugEnabled()) + log.debug("Authenticated " + subject); + final String username = subject.getPrincipals().iterator().next() + .getName(); + + // Logout callback when the display is disposed + display.disposeExec(new Runnable() { + public void run() { + log.debug("Display disposed"); + logout(loginContext, username); + } + }); + + // + // RUN THE WORKBENCH + // + Integer returnCode = null; + try { + returnCode = Subject.doAs(subject, new PrivilegedAction() { + public Integer run() { + RapWorkbenchAdvisor workbenchAdvisor = new RapWorkbenchAdvisor( + null); + int result = PlatformUI.createAndRunWorkbench(display, + workbenchAdvisor); + return new Integer(result); + } + }); + logout(loginContext, username); + if (log.isTraceEnabled()) + log.trace("Return code " + returnCode); + } finally { + display.dispose(); + } + return 1; + } + + private void logout(LoginContext loginContext, String username) { + try { + loginContext.logout(); + log.info("Logged out " + (username != null ? username : "") + + " (THREAD=" + Thread.currentThread().getId() + ")"); + } catch (LoginException e) { + log.error("Erorr when logging out", e); + } + } +} diff --git a/org.argeo.cms.ui.workbench.rap/src/org/argeo/security/ui/rap/RapActionBarAdvisor.java b/org.argeo.cms.ui.workbench.rap/src/org/argeo/security/ui/rap/RapActionBarAdvisor.java new file mode 100644 index 000000000..14bcee124 --- /dev/null +++ b/org.argeo.cms.ui.workbench.rap/src/org/argeo/security/ui/rap/RapActionBarAdvisor.java @@ -0,0 +1,232 @@ +/* + * 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.ui.rap; + +import org.argeo.cms.auth.CurrentUser; +import org.argeo.security.ui.commands.OpenHomePerspective; +import org.eclipse.core.commands.Category; +import org.eclipse.core.commands.Command; +import org.eclipse.jface.action.ICoolBarManager; +import org.eclipse.jface.action.IMenuManager; +import org.eclipse.jface.action.IToolBarManager; +import org.eclipse.jface.action.ToolBarManager; +import org.eclipse.swt.SWT; +import org.eclipse.ui.IWorkbenchWindow; +import org.eclipse.ui.actions.ActionFactory; +import org.eclipse.ui.actions.ActionFactory.IWorkbenchAction; +import org.eclipse.ui.application.ActionBarAdvisor; +import org.eclipse.ui.application.IActionBarConfigurer; +import org.eclipse.ui.commands.ICommandService; + +/** Eclipse rap specific action bar advisor */ +public class RapActionBarAdvisor extends ActionBarAdvisor { + private final static String ID_BASE = "org.argeo.security.ui.rap"; + // private final static Log log = LogFactory + // .getLog(SecureActionBarAdvisor.class); + + /** Null means anonymous */ + private String username = null; + + // private IAction logoutAction; + // private IWorkbenchAction openPerspectiveDialogAction; + // private IWorkbenchAction showViewMenuAction; + // private IWorkbenchAction preferences; + private IWorkbenchAction saveAction; + private IWorkbenchAction saveAllAction; + + // private IWorkbenchAction closeAllAction; + + public RapActionBarAdvisor(IActionBarConfigurer configurer, String username) { + super(configurer); + this.username = username; + } + + protected void makeActions(IWorkbenchWindow window) { + // preferences = ActionFactory.PREFERENCES.create(window); + // register(preferences); + // openPerspectiveDialogAction = ActionFactory.OPEN_PERSPECTIVE_DIALOG + // .create(window); + // register(openPerspectiveDialogAction); + // showViewMenuAction = ActionFactory.SHOW_VIEW_MENU.create(window); + // register(showViewMenuAction); + // + // // logout + // logoutAction = ActionFactory.QUIT.create(window); + // // logoutAction = createLogoutAction(); + // register(logoutAction); + // + // Save semantics + saveAction = ActionFactory.SAVE.create(window); + register(saveAction); + saveAllAction = ActionFactory.SAVE_ALL.create(window); + register(saveAllAction); + // closeAllAction = ActionFactory.CLOSE_ALL.create(window); + // register(closeAllAction); + + } + + protected void fillMenuBar(IMenuManager menuBar) { + // MenuManager fileMenu = new MenuManager("&File", + // IWorkbenchActionConstants.M_FILE); + // MenuManager editMenu = new MenuManager("&Edit", + // IWorkbenchActionConstants.M_EDIT); + // MenuManager windowMenu = new MenuManager("&Window", + // IWorkbenchActionConstants.M_WINDOW); + // + // menuBar.add(fileMenu); + // menuBar.add(editMenu); + // menuBar.add(windowMenu); + // // Add a group marker indicating where action set menus will appear. + // menuBar.add(new GroupMarker(IWorkbenchActionConstants.MB_ADDITIONS)); + // + // // File + // fileMenu.add(saveAction); + // fileMenu.add(saveAllAction); + // fileMenu.add(closeAllAction); + // fileMenu.add(new + // GroupMarker(IWorkbenchActionConstants.MB_ADDITIONS)); + // fileMenu.add(new Separator()); + // fileMenu.add(logoutAction); + // + // // Edit + // editMenu.add(preferences); + // + // // Window + // windowMenu.add(openPerspectiveDialogAction); + // windowMenu.add(showViewMenuAction); + } + + @Override + protected void fillCoolBar(ICoolBarManager coolBar) { + // Add a command which label is the display name of the current + // logged-in user + if (username != null) { + ICommandService cmdService = (ICommandService) getActionBarConfigurer() + .getWindowConfigurer().getWorkbenchConfigurer() + .getWorkbench().getService(ICommandService.class); + Category userMenus = cmdService.getCategory(ID_BASE + ".userMenus"); + if (!userMenus.isDefined()) + userMenus.define("User Menus", "User related menus"); + Command userMenu = cmdService.getCommand(ID_BASE + + ".userMenuCommand"); + if (userMenu.isDefined()) + userMenu.undefine(); + userMenu.define(CurrentUser.getDisplayName(), "User menu actions", + userMenus); + // userMenu.define(username, "User menu actions", userMenus); + + userMenu.setHandler(new OpenHomePerspective()); + + // userToolbar.add(new UserMenuAction()); + // coolBar.add(userToolbar); + } else {// anonymous + IToolBarManager userToolbar = new ToolBarManager(SWT.FLAT + | SWT.RIGHT); + // userToolbar.add(logoutAction); + coolBar.add(userToolbar); + } + // IToolBarManager saveToolbar = new ToolBarManager(SWT.FLAT | + // SWT.RIGHT); + // saveToolbar.add(saveAction); + // saveToolbar.add(saveAllAction); + // coolBar.add(saveToolbar); + } + + // class UserMenuAction extends Action implements IWorkbenchAction { + // + // public UserMenuAction() { + // super(username, IAction.AS_DROP_DOWN_MENU); + // // setMenuCreator(new UserMenu()); + // } + // + // @Override + // public String getId() { + // return "org.argeo.security.ui.rap.userMenu"; + // } + // + // @Override + // public void dispose() { + // } + // + // } + + // class UserMenu implements IMenuCreator { + // private Menu menu; + // + // public Menu getMenu(Control parent) { + // Menu menu = new Menu(parent); + // addActionToMenu(menu, logoutAction); + // return menu; + // } + // + // private void addActionToMenu(Menu menu, IAction action) { + // ActionContributionItem item = new ActionContributionItem(action); + // item.fill(menu, -1); + // } + // + // public void dispose() { + // if (menu != null) { + // menu.dispose(); + // } + // } + // + // public Menu getMenu(Menu parent) { + // // Not use + // return null; + // } + // + // } + + // protected IAction createLogoutAction() { + // Subject subject = Subject.getSubject(AccessController.getContext()); + // final String username = subject.getPrincipals().iterator().next() + // .getName(); + // + // IAction logoutAction = new Action() { + // public String getId() { + // return SecureRapActivator.ID + ".logoutAction"; + // } + // + // public String getText() { + // return "Logout " + username; + // } + // + // public void run() { + // // try { + // // Subject subject = SecureRapActivator.getLoginContext() + // // .getSubject(); + // // String subjectStr = subject.toString(); + // // subject.getPrincipals().clear(); + // // SecureRapActivator.getLoginContext().logout(); + // // log.info(subjectStr + " logged out"); + // // } catch (LoginException e) { + // // log.error("Error when logging out", e); + // // } + // // SecureEntryPoint.logout(username); + // // PlatformUI.getWorkbench().close(); + // // try { + // // RWT.getRequest().getSession().setMaxInactiveInterval(1); + // // } catch (Exception e) { + // // if (log.isTraceEnabled()) + // // log.trace("Error when invalidating session", e); + // // } + // } + // + // }; + // return logoutAction; + // } + +} diff --git a/org.argeo.cms.ui.workbench.rap/src/org/argeo/security/ui/rap/RapWindowAdvisor.java b/org.argeo.cms.ui.workbench.rap/src/org/argeo/security/ui/rap/RapWindowAdvisor.java new file mode 100644 index 000000000..05f4787f0 --- /dev/null +++ b/org.argeo.cms.ui.workbench.rap/src/org/argeo/security/ui/rap/RapWindowAdvisor.java @@ -0,0 +1,100 @@ +/* + * 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.ui.rap; + +import org.eclipse.swt.SWT; +import org.eclipse.swt.graphics.Point; +import org.eclipse.swt.graphics.Rectangle; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Event; +import org.eclipse.swt.widgets.Listener; +import org.eclipse.swt.widgets.Shell; +import org.eclipse.ui.IWorkbenchWindow; +import org.eclipse.ui.actions.ActionFactory; +import org.eclipse.ui.actions.ActionFactory.IWorkbenchAction; +import org.eclipse.ui.application.ActionBarAdvisor; +import org.eclipse.ui.application.IActionBarConfigurer; +import org.eclipse.ui.application.IWorkbenchWindowConfigurer; +import org.eclipse.ui.application.WorkbenchWindowAdvisor; + +/** Eclipse RAP specific window advisor */ +public class RapWindowAdvisor extends WorkbenchWindowAdvisor { + + private String username; + + public RapWindowAdvisor(IWorkbenchWindowConfigurer configurer, + String username) { + super(configurer); + this.username = username; + } + + @Override + public ActionBarAdvisor createActionBarAdvisor( + IActionBarConfigurer configurer) { + return new RapActionBarAdvisor(configurer, username); + } + + public void preWindowOpen() { + IWorkbenchWindowConfigurer configurer = getWindowConfigurer(); + configurer.setShowCoolBar(true); + configurer.setShowMenuBar(false); + configurer.setShowStatusLine(false); + configurer.setShowPerspectiveBar(true); + configurer.setTitle("Argeo Web UI"); //$NON-NLS-1$ + // Full screen, see + // http://wiki.eclipse.org/RAP/FAQ#How_to_create_a_fullscreen_application + configurer.setShellStyle(SWT.NO_TRIM); + Rectangle bounds = Display.getCurrent().getBounds(); + configurer.setInitialSize(new Point(bounds.width, bounds.height)); + + // Handle window resize in Rap 2.1+ see + // https://bugs.eclipse.org/bugs/show_bug.cgi?id=417254 + Display.getCurrent().addListener(SWT.Resize, new Listener() { + private static final long serialVersionUID = 2970912561866704526L; + + @Override + public void handleEvent(Event event) { + Rectangle bounds = event.display.getBounds(); + IWorkbenchWindow iww = getWindowConfigurer().getWindow() + .getWorkbench().getActiveWorkbenchWindow(); + iww.getShell().setBounds(bounds); + } + }); + } + + @Override + public void postWindowCreate() { + Shell shell = getWindowConfigurer().getWindow().getShell(); + shell.setMaximized(true); + } + + @Override + public void postWindowOpen() { + String defaultPerspective = getWindowConfigurer() + .getWorkbenchConfigurer().getWorkbench() + .getPerspectiveRegistry().getDefaultPerspective(); + if (defaultPerspective == null) { + IWorkbenchWindow window = getWindowConfigurer().getWindow(); + if (window == null) + return; + + IWorkbenchAction openPerspectiveDialogAction = ActionFactory.OPEN_PERSPECTIVE_DIALOG + .create(window); + openPerspectiveDialogAction.run(); + } + } + +} diff --git a/org.argeo.cms.ui.workbench.rap/src/org/argeo/security/ui/rap/RapWorkbenchAdvisor.java b/org.argeo.cms.ui.workbench.rap/src/org/argeo/security/ui/rap/RapWorkbenchAdvisor.java new file mode 100644 index 000000000..edde41f14 --- /dev/null +++ b/org.argeo.cms.ui.workbench.rap/src/org/argeo/security/ui/rap/RapWorkbenchAdvisor.java @@ -0,0 +1,64 @@ +/* + * 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.ui.rap; + +import org.eclipse.ui.IPerspectiveDescriptor; +import org.eclipse.ui.application.IWorkbenchConfigurer; +import org.eclipse.ui.application.IWorkbenchWindowConfigurer; +import org.eclipse.ui.application.WorkbenchAdvisor; +import org.eclipse.ui.application.WorkbenchWindowAdvisor; + +/** Eclipse RAP specific workbench advisor */ +public class RapWorkbenchAdvisor extends WorkbenchAdvisor { + public final static String INITIAL_PERSPECTIVE_PROPERTY = "org.argeo.security.ui.initialPerspective"; + public final static String SAVE_AND_RESTORE_PROPERTY = "org.argeo.security.ui.saveAndRestore"; + + private String initialPerspective = System.getProperty( + INITIAL_PERSPECTIVE_PROPERTY, null); + + private String username; + + public RapWorkbenchAdvisor(String username) { + this.username = username; + } + + @Override + public void initialize(IWorkbenchConfigurer configurer) { + super.initialize(configurer); + Boolean saveAndRestore = Boolean.parseBoolean(System.getProperty( + SAVE_AND_RESTORE_PROPERTY, "false")); + configurer.setSaveAndRestore(saveAndRestore); + } + + public WorkbenchWindowAdvisor createWorkbenchWindowAdvisor( + IWorkbenchWindowConfigurer configurer) { + return new RapWindowAdvisor(configurer, username); + } + + public String getInitialWindowPerspectiveId() { + if (initialPerspective != null) { + // check whether this user can see the declared perspective + // (typically the perspective won't be listed if this user doesn't + // have the right to see it) + IPerspectiveDescriptor pd = getWorkbenchConfigurer().getWorkbench() + .getPerspectiveRegistry() + .findPerspectiveWithId(initialPerspective); + if (pd == null) + return null; + } + return initialPerspective; + } +} diff --git a/org.argeo.cms.ui.workbench.rap/src/org/argeo/security/ui/rap/RapWorkbenchLogin.java b/org.argeo.cms.ui.workbench.rap/src/org/argeo/security/ui/rap/RapWorkbenchLogin.java new file mode 100644 index 000000000..7bf487a88 --- /dev/null +++ b/org.argeo.cms.ui.workbench.rap/src/org/argeo/security/ui/rap/RapWorkbenchLogin.java @@ -0,0 +1,93 @@ +package org.argeo.security.ui.rap; + +import java.security.PrivilegedAction; +import java.util.Locale; + +import javax.security.auth.Subject; +import javax.security.auth.x500.X500Principal; + +import org.argeo.cms.CmsMsg; +import org.argeo.cms.auth.CurrentUser; +import org.argeo.cms.util.CmsUtils; +import org.argeo.cms.util.LoginEntryPoint; +import org.eclipse.rap.rwt.RWT; +import org.eclipse.rap.rwt.client.service.JavaScriptExecutor; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.SelectionListener; +import org.eclipse.swt.widgets.Button; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Display; +import org.eclipse.ui.PlatformUI; + +public class RapWorkbenchLogin extends LoginEntryPoint { + // private final static Log log = + // LogFactory.getLog(RapWorkbenchLogin.class); + + /** Override to provide an application specific workbench advisor */ + protected RapWorkbenchAdvisor createRapWorkbenchAdvisor(String username) { + return new RapWorkbenchAdvisor(username); + } + + @Override + public int createUI() { + JavaScriptExecutor jsExecutor = RWT.getClient().getService( + JavaScriptExecutor.class); + int returnCode; + try { + returnCode = super.createUI(); + } finally { + // always reload + jsExecutor.execute("location.reload()"); + } + return returnCode; + } + + @Override + protected int postLogin() { + final Display display = Display.getCurrent(); + Subject subject = getSubject(); + if (subject.getPrincipals(X500Principal.class).isEmpty()) { + RWT.getClient().getService(JavaScriptExecutor.class) + .execute("location.reload()"); + } + // + // RUN THE WORKBENCH + // + Integer returnCode = null; + try { + returnCode = Subject.doAs(getSubject(), + new PrivilegedAction() { + public Integer run() { + int result = createAndRunWorkbench(display, + CurrentUser.getUsername(getSubject())); + return new Integer(result); + } + }); + // explicit workbench closing + logout(); + } finally { + display.dispose(); + } + return returnCode; + } + + protected int createAndRunWorkbench(Display display, String username) { + RapWorkbenchAdvisor workbenchAdvisor = createRapWorkbenchAdvisor(username); + return PlatformUI.createAndRunWorkbench(display, workbenchAdvisor); + } + + @Override + protected void extendsCredentialsBlock(Composite credentialsBlock, + Locale selectedLocale, SelectionListener loginSelectionListener) { + Button loginButton = new Button(credentialsBlock, SWT.PUSH); + loginButton.setText(CmsMsg.login.lead(selectedLocale)); + loginButton.setLayoutData(CmsUtils.fillWidth()); + loginButton.addSelectionListener(loginSelectionListener); + } + + @Override + protected Display createDisplay() { + return PlatformUI.createDisplay(); + } + +} diff --git a/org.argeo.cms.ui.workbench.rap/src/org/argeo/security/ui/rap/SecureEntryPoint.java b/org.argeo.cms.ui.workbench.rap/src/org/argeo/security/ui/rap/SecureEntryPoint.java new file mode 100644 index 000000000..a681527f7 --- /dev/null +++ b/org.argeo.cms.ui.workbench.rap/src/org/argeo/security/ui/rap/SecureEntryPoint.java @@ -0,0 +1,228 @@ +/* + * 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.ui.rap; + +import java.security.AccessControlContext; +import java.security.AccessController; +import java.security.PrivilegedAction; + +import javax.security.auth.Subject; +import javax.security.auth.login.CredentialNotFoundException; +import javax.security.auth.login.FailedLoginException; +import javax.security.auth.login.LoginContext; +import javax.security.auth.login.LoginException; +import javax.security.auth.x500.X500Principal; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpSession; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.argeo.cms.CmsException; +import org.argeo.cms.auth.AuthConstants; +import org.argeo.cms.auth.ThreadDeathLoginException; +import org.argeo.cms.widgets.auth.DefaultLoginDialog; +import org.argeo.eclipse.ui.dialogs.ErrorFeedback; +import org.eclipse.jface.dialogs.MessageDialog; +import org.eclipse.rap.rwt.RWT; +import org.eclipse.rap.rwt.application.EntryPoint; +import org.eclipse.swt.widgets.Display; +import org.eclipse.ui.PlatformUI; + +/** + * RAP entry point with login capabilities. Once the user has been + * authenticated, the workbench is run as a privileged action by the related + * subject. + */ +@Deprecated +public class SecureEntryPoint implements EntryPoint { + final static String ACCESS_CONTROL_CONTEXT = "org.argeo.node.accessControlContext"; + private final static Log log = LogFactory.getLog(SecureEntryPoint.class); + + /** + * How many seconds to wait before invalidating the session if the user has + * not yet logged in. + */ + private Integer loginTimeout = 1 * 60; + // TODO make it configurable + /** Default session timeout is 8 hours (European working day length) */ + private Integer sessionTimeout = 8 * 60 * 60; + + /** Override to provide an application specific workbench advisor */ + protected RapWorkbenchAdvisor createRapWorkbenchAdvisor(String username) { + return new RapWorkbenchAdvisor(username); + } + + @Override + public final int createUI() { + // Short login timeout so that the modal dialog login doesn't hang + // around too long + RWT.getRequest().getSession().setMaxInactiveInterval(loginTimeout); + + final Display display = PlatformUI.createDisplay(); + + // load context from session + HttpServletRequest httpRequest = RWT.getRequest(); + final HttpSession httpSession = httpRequest.getSession(); + AccessControlContext acc = (AccessControlContext) httpSession + .getAttribute(ACCESS_CONTROL_CONTEXT); + + final Subject subject; + if (acc != null + && Subject.getSubject(acc).getPrincipals(X500Principal.class) + .size() == 1) { + subject = Subject.getSubject(acc); + } else { + subject = new Subject(); + + final LoginContext loginContext; + DefaultLoginDialog callbackHandler; + try { + callbackHandler = new DefaultLoginDialog( + display.getActiveShell()); + loginContext = new LoginContext( + AuthConstants.LOGIN_CONTEXT_USER, subject, + callbackHandler); + } catch (LoginException e1) { + throw new CmsException("Cannot initialize login context", e1); + } + + tryLogin: while (subject.getPrincipals(X500Principal.class).size() == 0) { + try { + loginContext.login(); + if (subject.getPrincipals(X500Principal.class).size() == 0) + throw new CmsException("Login succeeded but no auth");// fatal + + // add thread locale to RWT session + // if (log.isTraceEnabled()) + // log.trace("Locale " + LocaleUtils.threadLocale.get()); + // RWT.setLocale(LocaleUtils.threadLocale.get()); + + // once the user is logged in, longer session timeout + RWT.getRequest().getSession() + .setMaxInactiveInterval(sessionTimeout); + + if (log.isDebugEnabled()) + log.debug("Authenticated " + subject); + } catch (FailedLoginException e) { + MessageDialog.openInformation(display.getActiveShell(), + "Bad Credentials", e.getMessage()); + // retry login + continue tryLogin; + } catch (CredentialNotFoundException e) { + MessageDialog.openInformation(display.getActiveShell(), + "No Credentials", e.getMessage()); + // retry login + continue tryLogin; + } catch (LoginException e) { + callbackHandler.getShell().dispose(); + return processLoginDeath(display, e); + } + } + } + final String username = subject.getPrincipals(X500Principal.class) + .iterator().next().getName(); + // Logout callback when the display is disposed + display.disposeExec(new Runnable() { + public void run() { + if (log.isTraceEnabled()) + log.trace("Display disposed"); + try { + LoginContext loginContext = new LoginContext( + AuthConstants.LOGIN_CONTEXT_USER, subject); + loginContext.logout(); + } catch (LoginException e) { + log.error("Error when logging out", e); + } + } + }); + + // + // RUN THE WORKBENCH + // + Integer returnCode = null; + try { + returnCode = Subject.doAs(subject, new PrivilegedAction() { + public Integer run() { + // add security context to session + httpSession.setAttribute(ACCESS_CONTROL_CONTEXT, + AccessController.getContext()); + + // start workbench + RapWorkbenchAdvisor workbenchAdvisor = createRapWorkbenchAdvisor(username); + int result = PlatformUI.createAndRunWorkbench(display, + workbenchAdvisor); + return new Integer(result); + } + }); + // Explicit exit from workbench + fullLogout(subject, username); + } finally { + display.dispose(); + } + return returnCode; + } + + private Integer processLoginDeath(Display display, Throwable e) { + // check thread death + ThreadDeath td = wasCausedByThreadDeath(e); + if (td != null) { + display.dispose(); + throw td; + } + if (!display.isDisposed()) { + ErrorFeedback.show("Unexpected exception during authentication", e); + // this was not just bad credentials or death thread + RWT.getRequest().getSession().setMaxInactiveInterval(1); + display.dispose(); + return -1; + } else { + throw new CmsException( + "Unexpected exception during authentication", e); + } + + } + + /** + * If there is a {@link ThreadDeath} in the root causes, rethrow it + * (important for RAP cleaning mechanism) + */ + protected ThreadDeath wasCausedByThreadDeath(Throwable t) { + if (t instanceof ThreadDeath) + return (ThreadDeath) t; + if (t instanceof ThreadDeathLoginException) + return ((ThreadDeathLoginException) t).getThreadDeath(); + if (t.getCause() != null) + return wasCausedByThreadDeath(t.getCause()); + else + return null; + } + + private void fullLogout(Subject subject, String username) { + try { + LoginContext loginContext = new LoginContext( + AuthConstants.LOGIN_CONTEXT_USER, subject); + loginContext.logout(); + HttpServletRequest httpRequest = RWT.getRequest(); + HttpSession httpSession = httpRequest.getSession(); + httpSession.setAttribute(ACCESS_CONTROL_CONTEXT, null); + RWT.getRequest().getSession().setMaxInactiveInterval(1); + log.info("Logged out " + (username != null ? username : "") + + " (THREAD=" + Thread.currentThread().getId() + ")"); + } catch (LoginException e) { + log.error("Error when logging out", e); + } + } +} diff --git a/org.argeo.cms.ui.workbench.rap/src/org/argeo/security/ui/rap/SecureRapActivator.java b/org.argeo.cms.ui.workbench.rap/src/org/argeo/security/ui/rap/SecureRapActivator.java new file mode 100644 index 000000000..b3d7c2337 --- /dev/null +++ b/org.argeo.cms.ui.workbench.rap/src/org/argeo/security/ui/rap/SecureRapActivator.java @@ -0,0 +1,38 @@ +/* + * 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.ui.rap; + +import org.osgi.framework.BundleActivator; +import org.osgi.framework.BundleContext; + +/** Configure Equinox login context from the bundle context. */ +public class SecureRapActivator implements BundleActivator { + public final static String ID = "org.argeo.security.ui.rap"; + + private static BundleContext bundleContext; + + public void start(BundleContext bc) throws Exception { + bundleContext = bc; + } + + public void stop(BundleContext context) throws Exception { + bundleContext = null; + } + + public static BundleContext getBundleContext() { + return bundleContext; + } +} diff --git a/org.argeo.cms.ui.workbench.rap/src/org/argeo/security/ui/rap/commands/OpenHome.java b/org.argeo.cms.ui.workbench.rap/src/org/argeo/security/ui/rap/commands/OpenHome.java new file mode 100644 index 000000000..37ebe3572 --- /dev/null +++ b/org.argeo.cms.ui.workbench.rap/src/org/argeo/security/ui/rap/commands/OpenHome.java @@ -0,0 +1,46 @@ +/* + * 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.ui.rap.commands; + +import org.argeo.eclipse.ui.dialogs.ErrorFeedback; +import org.argeo.eclipse.ui.workbench.CommandUtils; +import org.argeo.security.ui.UserHomePerspective; +import org.eclipse.core.commands.AbstractHandler; +import org.eclipse.core.commands.ExecutionEvent; +import org.eclipse.core.commands.ExecutionException; +import org.eclipse.ui.WorkbenchException; +import org.eclipse.ui.handlers.HandlerUtil; + +/** Default action of the user menu */ +public class OpenHome extends AbstractHandler { + private final static String PROP_OPEN_HOME_CMD_ID = "org.argeo.ui.openHomeCommandId"; + + public Object execute(ExecutionEvent event) throws ExecutionException { + + String defaultCmdId = System.getProperty(PROP_OPEN_HOME_CMD_ID, ""); + if (!"".equals(defaultCmdId.trim())) + CommandUtils.callCommand(defaultCmdId); + else { + try { + HandlerUtil.getActiveSite(event).getWorkbenchWindow() + .openPage(UserHomePerspective.ID, null); + } catch (WorkbenchException e) { + ErrorFeedback.show("Cannot open home perspective", e); + } + } + return null; + } +} diff --git a/org.argeo.cms.ui.workbench.rap/src/org/argeo/security/ui/rap/commands/UserMenu.java b/org.argeo.cms.ui.workbench.rap/src/org/argeo/security/ui/rap/commands/UserMenu.java new file mode 100644 index 000000000..98674303b --- /dev/null +++ b/org.argeo.cms.ui.workbench.rap/src/org/argeo/security/ui/rap/commands/UserMenu.java @@ -0,0 +1,30 @@ +/* + * 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.ui.rap.commands; + +import org.eclipse.core.commands.AbstractHandler; +import org.eclipse.core.commands.ExecutionEvent; +import org.eclipse.core.commands.ExecutionException; + +/** Default action of the user menu */ +public class UserMenu extends AbstractHandler { + + @Override + public Object execute(ExecutionEvent event) throws ExecutionException { + return null; + } + +} diff --git a/org.argeo.cms.ui.workbench/.classpath b/org.argeo.cms.ui.workbench/.classpath new file mode 100644 index 000000000..457b11571 --- /dev/null +++ b/org.argeo.cms.ui.workbench/.classpath @@ -0,0 +1,9 @@ + + + + + + + diff --git a/org.argeo.cms.ui.workbench/.project b/org.argeo.cms.ui.workbench/.project new file mode 100644 index 000000000..f7f7a8e6a --- /dev/null +++ b/org.argeo.cms.ui.workbench/.project @@ -0,0 +1,28 @@ + + + org.argeo.cms.ui.workbench + + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.pde.ManifestBuilder + + + + + org.eclipse.pde.SchemaBuilder + + + + + + org.eclipse.pde.PluginNature + org.eclipse.jdt.core.javanature + + diff --git a/org.argeo.cms.ui.workbench/META-INF/spring/commands.xml b/org.argeo.cms.ui.workbench/META-INF/spring/commands.xml new file mode 100644 index 000000000..7d3987644 --- /dev/null +++ b/org.argeo.cms.ui.workbench/META-INF/spring/commands.xml @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/org.argeo.cms.ui.workbench/META-INF/spring/common.xml b/org.argeo.cms.ui.workbench/META-INF/spring/common.xml new file mode 100644 index 000000000..f3b7ecb01 --- /dev/null +++ b/org.argeo.cms.ui.workbench/META-INF/spring/common.xml @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + osgibundle:security-admin.properties + + + + \ No newline at end of file diff --git a/org.argeo.cms.ui.workbench/META-INF/spring/osgi.xml b/org.argeo.cms.ui.workbench/META-INF/spring/osgi.xml new file mode 100644 index 000000000..02a63748f --- /dev/null +++ b/org.argeo.cms.ui.workbench/META-INF/spring/osgi.xml @@ -0,0 +1,17 @@ + + + + + + + + + \ No newline at end of file diff --git a/org.argeo.cms.ui.workbench/META-INF/spring/parts.xml b/org.argeo.cms.ui.workbench/META-INF/spring/parts.xml new file mode 100644 index 000000000..cbc36e0c3 --- /dev/null +++ b/org.argeo.cms.ui.workbench/META-INF/spring/parts.xml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/org.argeo.cms.ui.workbench/bnd.bnd b/org.argeo.cms.ui.workbench/bnd.bnd new file mode 100644 index 000000000..4ace8db0e --- /dev/null +++ b/org.argeo.cms.ui.workbench/bnd.bnd @@ -0,0 +1,16 @@ +Bundle-SymbolicName: org.argeo.cms.ui.workbench;singleton:=true +Bundle-Activator: org.argeo.security.ui.admin.SecurityAdminPlugin +Bundle-ActivationPolicy: lazy + +Require-Bundle: org.eclipse.core.runtime + +Import-Package: org.eclipse.core.runtime.jobs,\ +org.argeo.cms.auth,\ +org.argeo.eclipse.spring,\ +org.eclipse.jface.window,\ +org.eclipse.swt,\ +org.eclipse.swt.widgets,\ +org.eclipse.ui.services,\ +org.osgi.framework,\ +org.springframework.core,\ +* diff --git a/org.argeo.cms.ui.workbench/build.properties b/org.argeo.cms.ui.workbench/build.properties new file mode 100644 index 000000000..30f715358 --- /dev/null +++ b/org.argeo.cms.ui.workbench/build.properties @@ -0,0 +1 @@ +source.. = src/ diff --git a/org.argeo.cms.ui.workbench/icons/add.gif b/org.argeo.cms.ui.workbench/icons/add.gif new file mode 100644 index 000000000..252d7ebcb Binary files /dev/null and b/org.argeo.cms.ui.workbench/icons/add.gif differ diff --git a/org.argeo.cms.ui.workbench/icons/batch.gif b/org.argeo.cms.ui.workbench/icons/batch.gif new file mode 100644 index 000000000..b8ca14a8b Binary files /dev/null and b/org.argeo.cms.ui.workbench/icons/batch.gif differ diff --git a/org.argeo.cms.ui.workbench/icons/begin.gif b/org.argeo.cms.ui.workbench/icons/begin.gif new file mode 100755 index 000000000..feb8e94a7 Binary files /dev/null and b/org.argeo.cms.ui.workbench/icons/begin.gif differ diff --git a/org.argeo.cms.ui.workbench/icons/clear.gif b/org.argeo.cms.ui.workbench/icons/clear.gif new file mode 100644 index 000000000..6bc10f9d0 Binary files /dev/null and b/org.argeo.cms.ui.workbench/icons/clear.gif differ diff --git a/org.argeo.cms.ui.workbench/icons/commit.gif b/org.argeo.cms.ui.workbench/icons/commit.gif new file mode 100755 index 000000000..876f3eb16 Binary files /dev/null and b/org.argeo.cms.ui.workbench/icons/commit.gif differ diff --git a/org.argeo.cms.ui.workbench/icons/refresh.png b/org.argeo.cms.ui.workbench/icons/refresh.png new file mode 100644 index 000000000..a3884fb48 Binary files /dev/null and b/org.argeo.cms.ui.workbench/icons/refresh.png differ diff --git a/org.argeo.cms.ui.workbench/icons/remove.gif b/org.argeo.cms.ui.workbench/icons/remove.gif new file mode 100644 index 000000000..0ae6decd0 Binary files /dev/null and b/org.argeo.cms.ui.workbench/icons/remove.gif differ diff --git a/org.argeo.cms.ui.workbench/icons/role.gif b/org.argeo.cms.ui.workbench/icons/role.gif new file mode 100644 index 000000000..274a850e4 Binary files /dev/null and b/org.argeo.cms.ui.workbench/icons/role.gif differ diff --git a/org.argeo.cms.ui.workbench/icons/rollback.gif b/org.argeo.cms.ui.workbench/icons/rollback.gif new file mode 100755 index 000000000..c75399599 Binary files /dev/null and b/org.argeo.cms.ui.workbench/icons/rollback.gif differ diff --git a/org.argeo.cms.ui.workbench/icons/save.gif b/org.argeo.cms.ui.workbench/icons/save.gif new file mode 100644 index 000000000..654ad7b42 Binary files /dev/null and b/org.argeo.cms.ui.workbench/icons/save.gif differ diff --git a/org.argeo.cms.ui.workbench/icons/save_security.png b/org.argeo.cms.ui.workbench/icons/save_security.png new file mode 100644 index 000000000..ca41dc92b Binary files /dev/null and b/org.argeo.cms.ui.workbench/icons/save_security.png differ diff --git a/org.argeo.cms.ui.workbench/icons/save_security_disabled.png b/org.argeo.cms.ui.workbench/icons/save_security_disabled.png new file mode 100644 index 000000000..fb7d08d9a Binary files /dev/null and b/org.argeo.cms.ui.workbench/icons/save_security_disabled.png differ diff --git a/org.argeo.cms.ui.workbench/icons/security.gif b/org.argeo.cms.ui.workbench/icons/security.gif new file mode 100644 index 000000000..57fb95edc Binary files /dev/null and b/org.argeo.cms.ui.workbench/icons/security.gif differ diff --git a/org.argeo.cms.ui.workbench/icons/sync.gif b/org.argeo.cms.ui.workbench/icons/sync.gif new file mode 100644 index 000000000..b4fa052de Binary files /dev/null and b/org.argeo.cms.ui.workbench/icons/sync.gif differ diff --git a/org.argeo.cms.ui.workbench/icons/user.gif b/org.argeo.cms.ui.workbench/icons/user.gif new file mode 100644 index 000000000..90a00147b Binary files /dev/null and b/org.argeo.cms.ui.workbench/icons/user.gif differ diff --git a/org.argeo.cms.ui.workbench/icons/users.gif b/org.argeo.cms.ui.workbench/icons/users.gif new file mode 100644 index 000000000..2de7edd64 Binary files /dev/null and b/org.argeo.cms.ui.workbench/icons/users.gif differ diff --git a/org.argeo.cms.ui.workbench/plugin.xml b/org.argeo.cms.ui.workbench/plugin.xml new file mode 100644 index 000000000..2cf0ba210 --- /dev/null +++ b/org.argeo.cms.ui.workbench/plugin.xml @@ -0,0 +1,230 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/org.argeo.cms.ui.workbench/pom.xml b/org.argeo.cms.ui.workbench/pom.xml new file mode 100644 index 000000000..ead8e2e84 --- /dev/null +++ b/org.argeo.cms.ui.workbench/pom.xml @@ -0,0 +1,46 @@ + + + 4.0.0 + + org.argeo.commons + 2.1.46-SNAPSHOT + argeo-commons + .. + + org.argeo.cms.ui.workbench + CMS Workbench + jar + + + org.argeo.commons + org.argeo.cms + 2.1.46-SNAPSHOT + + + org.argeo.commons + org.argeo.util + 2.1.46-SNAPSHOT + + + org.argeo.commons + org.argeo.security.ui + 2.1.46-SNAPSHOT + + + org.argeo.commons + org.argeo.enterprise + 2.1.46-SNAPSHOT + + + org.argeo.commons + org.argeo.eclipse.ui + 2.1.46-SNAPSHOT + + + org.argeo.commons + org.argeo.eclipse.ui.rap + 2.1.46-SNAPSHOT + provided + + + \ No newline at end of file diff --git a/org.argeo.cms.ui.workbench/security-admin.properties b/org.argeo.cms.ui.workbench/security-admin.properties new file mode 100644 index 000000000..e69de29bb diff --git a/org.argeo.cms.ui.workbench/src/org/argeo/security/ui/admin/SecurityAdminImages.java b/org.argeo.cms.ui.workbench/src/org/argeo/security/ui/admin/SecurityAdminImages.java new file mode 100644 index 000000000..f15f8ec9a --- /dev/null +++ b/org.argeo.cms.ui.workbench/src/org/argeo/security/ui/admin/SecurityAdminImages.java @@ -0,0 +1,47 @@ +/* + * Argeo Connect - Data management and communications + * Copyright (C) 2012 Argeo GmbH + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * + * Additional permission under GNU GPL version 3 section 7 + * + * If you modify this Program, or any covered work, by linking or combining it + * with software covered by the terms of the Eclipse Public License, the + * licensors of this Program grant you additional permission to convey the + * resulting work. Corresponding Source for a non-source form of such a + * combination shall include the source code for the parts of such software + * which are used as well as that of the covered work. + */ +package org.argeo.security.ui.admin; + +import org.eclipse.jface.resource.ImageDescriptor; +import org.eclipse.swt.graphics.Image; + +/** Shared icons that must be declared programmatically . */ +public class SecurityAdminImages { + private final static String PREFIX = "icons/"; + + public final static ImageDescriptor ICON_REMOVE_DESC = SecurityAdminPlugin + .getImageDescriptor(PREFIX + "remove.gif"); + public final static ImageDescriptor ICON_USER_DESC = SecurityAdminPlugin + .getImageDescriptor(PREFIX + "user.gif"); + + public final static Image ICON_USER = ICON_USER_DESC.createImage(); + public final static Image ICON_GROUP = SecurityAdminPlugin + .getImageDescriptor(PREFIX + "users.gif").createImage(); + public final static Image ICON_ROLE = SecurityAdminPlugin + .getImageDescriptor(PREFIX + "role.gif").createImage(); + +} diff --git a/org.argeo.cms.ui.workbench/src/org/argeo/security/ui/admin/SecurityAdminPerspective.java b/org.argeo.cms.ui.workbench/src/org/argeo/security/ui/admin/SecurityAdminPerspective.java new file mode 100644 index 000000000..2334827c9 --- /dev/null +++ b/org.argeo.cms.ui.workbench/src/org/argeo/security/ui/admin/SecurityAdminPerspective.java @@ -0,0 +1,36 @@ +/* + * 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.ui.admin; + +import org.argeo.security.ui.admin.internal.parts.GroupsView; +import org.argeo.security.ui.admin.internal.parts.UsersView; +import org.eclipse.ui.IFolderLayout; +import org.eclipse.ui.IPageLayout; +import org.eclipse.ui.IPerspectiveFactory; + +/** Default perspective to manage users and groups */ +public class SecurityAdminPerspective implements IPerspectiveFactory { + public void createInitialLayout(IPageLayout layout) { + String editorArea = layout.getEditorArea(); + layout.setEditorAreaVisible(true); + layout.setFixed(false); + + IFolderLayout left = layout.createFolder("left", IPageLayout.LEFT, + 0.25f, editorArea); + left.addView(UsersView.ID); + left.addView(GroupsView.ID); + } +} diff --git a/org.argeo.cms.ui.workbench/src/org/argeo/security/ui/admin/SecurityAdminPlugin.java b/org.argeo.cms.ui.workbench/src/org/argeo/security/ui/admin/SecurityAdminPlugin.java new file mode 100644 index 000000000..f9c0ad9af --- /dev/null +++ b/org.argeo.cms.ui.workbench/src/org/argeo/security/ui/admin/SecurityAdminPlugin.java @@ -0,0 +1,54 @@ +/* + * 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.ui.admin; + +import org.eclipse.jface.resource.ImageDescriptor; +import org.eclipse.ui.plugin.AbstractUIPlugin; +import org.osgi.framework.BundleContext; + +public class SecurityAdminPlugin extends AbstractUIPlugin { + public static final String PLUGIN_ID = "org.argeo.security.ui.admin"; //$NON-NLS-1$ + private static SecurityAdminPlugin plugin; + private static BundleContext bundleContext; + + public SecurityAdminPlugin() { + } + + public void start(BundleContext context) throws Exception { + super.start(context); + plugin = this; + bundleContext = context; + } + + public void stop(BundleContext context) throws Exception { + plugin = null; + bundleContext = null; + super.stop(context); + } + + public static SecurityAdminPlugin getDefault() { + return plugin; + } + + public static BundleContext getBundleContext() { + return bundleContext; + } + + public static ImageDescriptor getImageDescriptor(String path) { + return imageDescriptorFromPlugin(PLUGIN_ID, path); + } + +} diff --git a/org.argeo.cms.ui.workbench/src/org/argeo/security/ui/admin/internal/PartStateChanged.java b/org.argeo.cms.ui.workbench/src/org/argeo/security/ui/admin/internal/PartStateChanged.java new file mode 100644 index 000000000..aacf6ef63 --- /dev/null +++ b/org.argeo.cms.ui.workbench/src/org/argeo/security/ui/admin/internal/PartStateChanged.java @@ -0,0 +1,58 @@ +package org.argeo.security.ui.admin.internal; + +import org.argeo.cms.CmsException; +import org.eclipse.swt.widgets.Display; +import org.eclipse.ui.IPartListener; +import org.eclipse.ui.IStartup; +import org.eclipse.ui.IWorkbenchPage; +import org.eclipse.ui.IWorkbenchPart; +import org.eclipse.ui.PlatformUI; + +/** Manage transaction and part refresh while updating the security model */ +public class PartStateChanged implements IPartListener, IStartup { + // private final static Log log = LogFactory.getLog(PartStateChanged.class); + // private IContextActivation contextActivation; + + @Override + public void earlyStartup() { + Display.getDefault().asyncExec(new Runnable() { + public void run() { + try { + IWorkbenchPage iwp = PlatformUI.getWorkbench() + .getActiveWorkbenchWindow().getActivePage(); + if (iwp != null) + iwp.addPartListener(new PartStateChanged()); + } catch (Exception e) { + throw new CmsException( + "Error while registering the PartStateChangedListener", + e); + } + } + }); + } + + @Override + public void partActivated(IWorkbenchPart part) { + // Nothing to do + } + + @Override + public void partBroughtToTop(IWorkbenchPart part) { + // Nothing to do + } + + @Override + public void partClosed(IWorkbenchPart part) { + // Nothing to do + } + + @Override + public void partDeactivated(IWorkbenchPart part) { + // Nothing to do + } + + @Override + public void partOpened(IWorkbenchPart part) { + // Nothing to do + } +} \ No newline at end of file diff --git a/org.argeo.cms.ui.workbench/src/org/argeo/security/ui/admin/internal/UiAdminUtils.java b/org.argeo.cms.ui.workbench/src/org/argeo/security/ui/admin/internal/UiAdminUtils.java new file mode 100644 index 000000000..871877a0e --- /dev/null +++ b/org.argeo.cms.ui.workbench/src/org/argeo/security/ui/admin/internal/UiAdminUtils.java @@ -0,0 +1,32 @@ +package org.argeo.security.ui.admin.internal; + +import javax.transaction.UserTransaction; + +import org.argeo.cms.CmsException; +import org.argeo.security.ui.admin.internal.providers.UserTransactionProvider; +import org.eclipse.ui.IWorkbenchWindow; +import org.eclipse.ui.PlatformUI; +import org.eclipse.ui.services.ISourceProviderService; + +/** First effort to centralize back end methods used by the user admin UI */ +public class UiAdminUtils { + /* + * INTERNAL METHODS: Below methods are meant to stay here and are not part + * of a potential generic backend to manage the useradmin + */ + /** Easily notify the ActiveWindow that the transaction had a state change */ + public final static void notifyTransactionStateChange( + UserTransaction userTransaction) { + try { + IWorkbenchWindow aww = PlatformUI.getWorkbench() + .getActiveWorkbenchWindow(); + ISourceProviderService sourceProviderService = (ISourceProviderService) aww + .getService(ISourceProviderService.class); + UserTransactionProvider esp = (UserTransactionProvider) sourceProviderService + .getSourceProvider(UserTransactionProvider.TRANSACTION_STATE); + esp.fireTransactionStateChange(); + } catch (Exception e) { + throw new CmsException("Unable to begin transaction", e); + } + } +} \ No newline at end of file diff --git a/org.argeo.cms.ui.workbench/src/org/argeo/security/ui/admin/internal/UiUserAdminListener.java b/org.argeo.cms.ui.workbench/src/org/argeo/security/ui/admin/internal/UiUserAdminListener.java new file mode 100644 index 000000000..98c983683 --- /dev/null +++ b/org.argeo.cms.ui.workbench/src/org/argeo/security/ui/admin/internal/UiUserAdminListener.java @@ -0,0 +1,27 @@ +package org.argeo.security.ui.admin.internal; + +import org.eclipse.swt.widgets.Display; +import org.osgi.service.useradmin.UserAdminEvent; +import org.osgi.service.useradmin.UserAdminListener; + +/** Convenience class to insure the call to refresh is done in the UI thread */ +public abstract class UiUserAdminListener implements UserAdminListener { + + private final Display display; + + public UiUserAdminListener(Display display) { + this.display = display; + } + + @Override + public void roleChanged(final UserAdminEvent event) { + display.asyncExec(new Runnable() { + @Override + public void run() { + roleChangedToUiThread(event); + } + }); + } + + public abstract void roleChangedToUiThread(UserAdminEvent event); +} diff --git a/org.argeo.cms.ui.workbench/src/org/argeo/security/ui/admin/internal/UserAdminWrapper.java b/org.argeo.cms.ui.workbench/src/org/argeo/security/ui/admin/internal/UserAdminWrapper.java new file mode 100644 index 000000000..d443a3d47 --- /dev/null +++ b/org.argeo.cms.ui.workbench/src/org/argeo/security/ui/admin/internal/UserAdminWrapper.java @@ -0,0 +1,77 @@ +package org.argeo.security.ui.admin.internal; + +import java.util.ArrayList; +import java.util.List; + +import javax.transaction.Status; +import javax.transaction.UserTransaction; + +import org.argeo.cms.CmsException; +import org.osgi.service.useradmin.UserAdminEvent; +import org.osgi.service.useradmin.UserAdminListener; + +/** Centralise interaction with the UserAdmin in this bundle */ +public class UserAdminWrapper extends + org.argeo.cms.util.useradmin.UserAdminWrapper { + + // First effort to simplify UX while managing users and groups + public final static boolean COMMIT_ON_SAVE = true; + + // Registered listeners + List listeners = new ArrayList(); + + /** + * Starts a transaction if necessary. Should always been called together + * with {@link UserAdminWrapper#commitOrNotifyTransactionStateChange()} once + * the security model changes have been performed. + */ + public UserTransaction beginTransactionIfNeeded() { + try { + UserTransaction userTransaction = getUserTransaction(); + if (userTransaction.getStatus() == Status.STATUS_NO_TRANSACTION) { + userTransaction.begin(); + // UiAdminUtils.notifyTransactionStateChange(userTransaction); + } + return userTransaction; + } catch (Exception e) { + throw new CmsException("Unable to begin transaction", e); + } + } + + /** + * Depending on the current application configuration, it will either commit + * the current transaction or throw a notification that the transaction + * state has changed (In the later case, it must be called from the UI + * thread). + */ + public void commitOrNotifyTransactionStateChange() { + try { + UserTransaction userTransaction = getUserTransaction(); + if (userTransaction.getStatus() == Status.STATUS_NO_TRANSACTION) + return; + + if (UserAdminWrapper.COMMIT_ON_SAVE) + userTransaction.commit(); + else + UiAdminUtils.notifyTransactionStateChange(userTransaction); + } catch (Exception e) { + throw new CmsException("Unable to clean transaction", e); + } + } + + // TODO implement safer mechanism + public void addListener(UserAdminListener userAdminListener) { + if (!listeners.contains(userAdminListener)) + listeners.add(userAdminListener); + } + + public void removeListener(UserAdminListener userAdminListener) { + if (listeners.contains(userAdminListener)) + listeners.remove(userAdminListener); + } + + public void notifyListeners(UserAdminEvent event) { + for (UserAdminListener listener : listeners) + listener.roleChanged(event); + } +} \ No newline at end of file diff --git a/org.argeo.cms.ui.workbench/src/org/argeo/security/ui/admin/internal/commands/DeleteGroups.java b/org.argeo.cms.ui.workbench/src/org/argeo/security/ui/admin/internal/commands/DeleteGroups.java new file mode 100644 index 000000000..df5d430ab --- /dev/null +++ b/org.argeo.cms.ui.workbench/src/org/argeo/security/ui/admin/internal/commands/DeleteGroups.java @@ -0,0 +1,100 @@ +/* + * 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.ui.admin.internal.commands; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +import org.argeo.cms.util.useradmin.UserAdminUtils; +import org.argeo.security.ui.admin.SecurityAdminPlugin; +import org.argeo.security.ui.admin.internal.UserAdminWrapper; +import org.argeo.security.ui.admin.internal.parts.UserEditorInput; +import org.eclipse.core.commands.AbstractHandler; +import org.eclipse.core.commands.ExecutionEvent; +import org.eclipse.core.commands.ExecutionException; +import org.eclipse.jface.dialogs.MessageDialog; +import org.eclipse.jface.viewers.ISelection; +import org.eclipse.jface.viewers.IStructuredSelection; +import org.eclipse.ui.IEditorPart; +import org.eclipse.ui.IWorkbenchPage; +import org.eclipse.ui.handlers.HandlerUtil; +import org.osgi.service.useradmin.Group; +import org.osgi.service.useradmin.UserAdmin; +import org.osgi.service.useradmin.UserAdminEvent; + +/** Delete the selected groups */ +public class DeleteGroups extends AbstractHandler { + public final static String ID = SecurityAdminPlugin.PLUGIN_ID + + ".deleteGroups"; + + /* DEPENDENCY INJECTION */ + private UserAdminWrapper userAdminWrapper; + + @SuppressWarnings("unchecked") + public Object execute(ExecutionEvent event) throws ExecutionException { + ISelection selection = HandlerUtil.getCurrentSelection(event); + if (selection.isEmpty()) + return null; + + List groups = new ArrayList(); + Iterator it = ((IStructuredSelection) selection).iterator(); + StringBuilder builder = new StringBuilder(); + while (it.hasNext()) { + Group currGroup = it.next(); + String groupName = UserAdminUtils.getUsername(currGroup); + // TODO add checks + builder.append(groupName).append("; "); + groups.add(currGroup); + } + + if (!MessageDialog.openQuestion(HandlerUtil.getActiveShell(event), + "Delete Groups", + "Are you sure that you " + "want to delete these groups?\n" + + builder.substring(0, builder.length() - 2))) + return null; + + userAdminWrapper.beginTransactionIfNeeded(); + UserAdmin userAdmin = userAdminWrapper.getUserAdmin(); + IWorkbenchPage iwp = HandlerUtil.getActiveWorkbenchWindow(event) + .getActivePage(); + for (Group group : groups) { + String groupName = group.getName(); + // TODO find a way to close the editor cleanly if opened. Cannot be + // done through the UserAdminListeners, it causes a + // java.util.ConcurrentModificationException because disposing the + // editor unregisters and disposes the listener + IEditorPart part = iwp.findEditor(new UserEditorInput(groupName)); + if (part != null) + iwp.closeEditor(part, false); + userAdmin.removeRole(groupName); + } + userAdminWrapper.commitOrNotifyTransactionStateChange(); + + // Update the view + for (Group group : groups) { + userAdminWrapper.notifyListeners(new UserAdminEvent(null, + UserAdminEvent.ROLE_REMOVED, group)); + } + + return null; + } + + /* DEPENDENCY INJECTION */ + public void setUserAdminWrapper(UserAdminWrapper userAdminWrapper) { + this.userAdminWrapper = userAdminWrapper; + } +} \ No newline at end of file diff --git a/org.argeo.cms.ui.workbench/src/org/argeo/security/ui/admin/internal/commands/DeleteUsers.java b/org.argeo.cms.ui.workbench/src/org/argeo/security/ui/admin/internal/commands/DeleteUsers.java new file mode 100644 index 000000000..e583bef53 --- /dev/null +++ b/org.argeo.cms.ui.workbench/src/org/argeo/security/ui/admin/internal/commands/DeleteUsers.java @@ -0,0 +1,106 @@ +/* + * 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.ui.admin.internal.commands; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +import org.argeo.cms.util.useradmin.UserAdminUtils; +import org.argeo.security.ui.admin.SecurityAdminPlugin; +import org.argeo.security.ui.admin.internal.UserAdminWrapper; +import org.argeo.security.ui.admin.internal.parts.UserEditorInput; +import org.eclipse.core.commands.AbstractHandler; +import org.eclipse.core.commands.ExecutionEvent; +import org.eclipse.core.commands.ExecutionException; +import org.eclipse.jface.dialogs.MessageDialog; +import org.eclipse.jface.viewers.ISelection; +import org.eclipse.jface.viewers.IStructuredSelection; +import org.eclipse.ui.IEditorPart; +import org.eclipse.ui.IWorkbenchPage; +import org.eclipse.ui.handlers.HandlerUtil; +import org.osgi.service.useradmin.User; +import org.osgi.service.useradmin.UserAdmin; +import org.osgi.service.useradmin.UserAdminEvent; + +/** Delete the selected users */ +public class DeleteUsers extends AbstractHandler { + public final static String ID = SecurityAdminPlugin.PLUGIN_ID + + ".deleteUsers"; + + /* DEPENDENCY INJECTION */ + private UserAdminWrapper userAdminWrapper; + + @SuppressWarnings("unchecked") + public Object execute(ExecutionEvent event) throws ExecutionException { + ISelection selection = HandlerUtil.getCurrentSelection(event); + if (selection.isEmpty()) + return null; + + Iterator it = ((IStructuredSelection) selection).iterator(); + List users = new ArrayList(); + StringBuilder builder = new StringBuilder(); + + while (it.hasNext()) { + User currUser = it.next(); + String userName = UserAdminUtils.getUsername(currUser); + if (UserAdminUtils.isCurrentUser(currUser)) { + MessageDialog.openError(HandlerUtil.getActiveShell(event), + "Deletion forbidden", + "You cannot delete your own user this way."); + return null; + } + builder.append(userName).append("; "); + users.add(currUser); + } + + if (!MessageDialog.openQuestion( + HandlerUtil.getActiveShell(event), + "Delete Users", + "Are you sure that you want to delete these users?\n" + + builder.substring(0, builder.length() - 2))) + return null; + + userAdminWrapper.beginTransactionIfNeeded(); + UserAdmin userAdmin = userAdminWrapper.getUserAdmin(); + IWorkbenchPage iwp = HandlerUtil.getActiveWorkbenchWindow(event) + .getActivePage(); + + for (User user : users) { + String userName = user.getName(); + // TODO find a way to close the editor cleanly if opened. Cannot be + // done through the UserAdminListeners, it causes a + // java.util.ConcurrentModificationException because disposing the + // editor unregisters and disposes the listener + IEditorPart part = iwp.findEditor(new UserEditorInput(userName)); + if (part != null) + iwp.closeEditor(part, false); + userAdmin.removeRole(userName); + } + userAdminWrapper.commitOrNotifyTransactionStateChange(); + + for (User user : users) { + userAdminWrapper.notifyListeners(new UserAdminEvent(null, + UserAdminEvent.ROLE_REMOVED, user)); + } + return null; + } + + /* DEPENDENCY INJECTION */ + public void setUserAdminWrapper(UserAdminWrapper userAdminWrapper) { + this.userAdminWrapper = userAdminWrapper; + } +} \ No newline at end of file diff --git a/org.argeo.cms.ui.workbench/src/org/argeo/security/ui/admin/internal/commands/ForceRefresh.java b/org.argeo.cms.ui.workbench/src/org/argeo/security/ui/admin/internal/commands/ForceRefresh.java new file mode 100644 index 000000000..231c8718a --- /dev/null +++ b/org.argeo.cms.ui.workbench/src/org/argeo/security/ui/admin/internal/commands/ForceRefresh.java @@ -0,0 +1,43 @@ +/* + * 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.ui.admin.internal.commands; + +import org.argeo.security.ui.admin.internal.parts.GroupsView; +import org.argeo.security.ui.admin.internal.parts.UsersView; +import org.eclipse.core.commands.AbstractHandler; +import org.eclipse.core.commands.ExecutionEvent; +import org.eclipse.core.commands.ExecutionException; +import org.eclipse.ui.IWorkbenchPage; +import org.eclipse.ui.IWorkbenchPart; +import org.eclipse.ui.IWorkbenchWindow; +import org.eclipse.ui.handlers.HandlerUtil; + +/** Retrieve the active view or editor and call forceRefresh method if defined */ +public class ForceRefresh extends AbstractHandler { + + public Object execute(ExecutionEvent event) throws ExecutionException { + IWorkbenchWindow iww = HandlerUtil.getActiveWorkbenchWindow(event); + if (iww == null) + return null; + IWorkbenchPage activePage = iww.getActivePage(); + IWorkbenchPart part = activePage.getActivePart(); + if (part instanceof UsersView) + ((UsersView) part).refresh(); + else if (part instanceof GroupsView) + ((GroupsView) part).refresh(); + return null; + } +} \ No newline at end of file diff --git a/org.argeo.cms.ui.workbench/src/org/argeo/security/ui/admin/internal/commands/NewGroup.java b/org.argeo.cms.ui.workbench/src/org/argeo/security/ui/admin/internal/commands/NewGroup.java new file mode 100644 index 000000000..75b9b0b33 --- /dev/null +++ b/org.argeo.cms.ui.workbench/src/org/argeo/security/ui/admin/internal/commands/NewGroup.java @@ -0,0 +1,240 @@ +/* + * 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.ui.admin.internal.commands; + +import java.util.Dictionary; +import java.util.Map; + +import org.argeo.cms.CmsException; +import org.argeo.eclipse.ui.EclipseUiUtils; +import org.argeo.eclipse.ui.dialogs.ErrorFeedback; +import org.argeo.jcr.ArgeoNames; +import org.argeo.osgi.useradmin.LdifName; +import org.argeo.osgi.useradmin.UserAdminConf; +import org.argeo.security.ui.admin.SecurityAdminPlugin; +import org.argeo.security.ui.admin.internal.UserAdminWrapper; +import org.eclipse.core.commands.AbstractHandler; +import org.eclipse.core.commands.ExecutionEvent; +import org.eclipse.core.commands.ExecutionException; +import org.eclipse.jface.wizard.Wizard; +import org.eclipse.jface.wizard.WizardDialog; +import org.eclipse.jface.wizard.WizardPage; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.FocusEvent; +import org.eclipse.swt.events.FocusListener; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Combo; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.Text; +import org.eclipse.ui.handlers.HandlerUtil; +import org.osgi.service.useradmin.Group; +import org.osgi.service.useradmin.Role; +import org.osgi.service.useradmin.UserAdminEvent; + +/** Create a new group */ +public class NewGroup extends AbstractHandler { + public final static String ID = SecurityAdminPlugin.PLUGIN_ID + ".newGroup"; + + /* DEPENDENCY INJECTION */ + private UserAdminWrapper userAdminWrapper; + + public Object execute(ExecutionEvent event) throws ExecutionException { + NewGroupWizard newGroupWizard = new NewGroupWizard(); + newGroupWizard.setWindowTitle("Group creation"); + WizardDialog dialog = new WizardDialog( + HandlerUtil.getActiveShell(event), newGroupWizard); + dialog.open(); + return null; + } + + private class NewGroupWizard extends Wizard { + + // Pages + private MainGroupInfoWizardPage mainGroupInfo; + + // UI fields + private Text dNameTxt, commonNameTxt, descriptionTxt; + private Combo baseDnCmb; + + public NewGroupWizard() { + } + + @Override + public void addPages() { + mainGroupInfo = new MainGroupInfoWizardPage(); + addPage(mainGroupInfo); + } + + @SuppressWarnings({ "rawtypes", "unchecked" }) + @Override + public boolean performFinish() { + if (!canFinish()) + return false; + String commonName = commonNameTxt.getText(); + try { + userAdminWrapper.beginTransactionIfNeeded(); + String dn = getDn(commonName); + Group group = (Group) userAdminWrapper.getUserAdmin() + .createRole(dn, Role.GROUP); + Dictionary props = group.getProperties(); + String descStr = descriptionTxt.getText(); + if (EclipseUiUtils.notEmpty(descStr)) + props.put(LdifName.description.name(), descStr); + userAdminWrapper.commitOrNotifyTransactionStateChange(); + userAdminWrapper.notifyListeners(new UserAdminEvent(null, + UserAdminEvent.ROLE_CREATED, group)); + return true; + } catch (Exception e) { + ErrorFeedback.show("Cannot create new group " + commonName, e); + return false; + } + } + + private class MainGroupInfoWizardPage extends WizardPage implements + FocusListener, ArgeoNames { + private static final long serialVersionUID = -3150193365151601807L; + + public MainGroupInfoWizardPage() { + super("Main"); + setTitle("General information"); + setMessage("Please choose a domain, provide a common name " + + "and a free description"); + } + + @Override + public void createControl(Composite parent) { + Composite bodyCmp = new Composite(parent, SWT.NONE); + setControl(bodyCmp); + bodyCmp.setLayout(new GridLayout(2, false)); + + dNameTxt = EclipseUiUtils.createGridLT(bodyCmp, + "Distinguished name"); + dNameTxt.setEnabled(false); + + baseDnCmb = createGridLC(bodyCmp, "Base DN"); + // Initialise before adding the listener to avoid NPE + initialiseDnCmb(baseDnCmb); + baseDnCmb.addFocusListener(this); + + commonNameTxt = EclipseUiUtils.createGridLT(bodyCmp, + "Common name"); + commonNameTxt.addFocusListener(this); + + Label descLbl = new Label(bodyCmp, SWT.LEAD); + descLbl.setText("Description"); + descLbl.setLayoutData(new GridData(SWT.RIGHT, SWT.TOP, false, + false)); + descriptionTxt = new Text(bodyCmp, SWT.LEAD | SWT.MULTI + | SWT.WRAP | SWT.BORDER); + descriptionTxt.setLayoutData(EclipseUiUtils.fillAll()); + descriptionTxt.addFocusListener(this); + + // Initialize buttons + setPageComplete(false); + getContainer().updateButtons(); + } + + @Override + public void focusLost(FocusEvent event) { + String name = commonNameTxt.getText(); + if (EclipseUiUtils.isEmpty(name)) + dNameTxt.setText(""); + else + dNameTxt.setText(getDn(name)); + + String message = checkComplete(); + if (message != null) { + setMessage(message, WizardPage.ERROR); + setPageComplete(false); + } else { + setMessage("Complete", WizardPage.INFORMATION); + setPageComplete(true); + } + getContainer().updateButtons(); + } + + @Override + public void focusGained(FocusEvent event) { + } + + /** @return the error message or null if complete */ + protected String checkComplete() { + String name = commonNameTxt.getText(); + + if (name.trim().equals("")) + return "Common name must not be empty"; + Role role = userAdminWrapper.getUserAdmin() + .getRole(getDn(name)); + if (role != null) + return "Group " + name + " already exists"; + return null; + } + + @Override + public void setVisible(boolean visible) { + super.setVisible(visible); + if (visible) + if (baseDnCmb.getSelectionIndex() == -1) + baseDnCmb.setFocus(); + else + commonNameTxt.setFocus(); + } + } + + private Map getDns() { + return userAdminWrapper.getKnownBaseDns(true); + } + + private String getDn(String cn) { + Map dns = getDns(); + String bdn = baseDnCmb.getText(); + if (EclipseUiUtils.notEmpty(bdn)) { + Dictionary props = UserAdminConf.uriAsProperties(dns + .get(bdn)); + String dn = LdifName.cn.name() + "=" + cn + "," + + UserAdminConf.groupBase.getValue(props) + "," + bdn; + return dn; + } + return null; + } + + private void initialiseDnCmb(Combo combo) { + Map dns = userAdminWrapper.getKnownBaseDns(true); + if (dns.isEmpty()) + throw new CmsException( + "No writable base dn found. Cannot create group"); + combo.setItems(dns.keySet().toArray(new String[0])); + if (dns.size() == 1) + combo.select(0); + } + } + + private Combo createGridLC(Composite parent, String label) { + Label lbl = new Label(parent, SWT.LEAD); + lbl.setText(label); + lbl.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false)); + Combo combo = new Combo(parent, SWT.LEAD | SWT.BORDER | SWT.READ_ONLY); + combo.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false)); + return combo; + } + + /* DEPENDENCY INJECTION */ + public void setUserAdminWrapper(UserAdminWrapper userAdminWrapper) { + this.userAdminWrapper = userAdminWrapper; + } +} \ No newline at end of file diff --git a/org.argeo.cms.ui.workbench/src/org/argeo/security/ui/admin/internal/commands/NewUser.java b/org.argeo.cms.ui.workbench/src/org/argeo/security/ui/admin/internal/commands/NewUser.java new file mode 100644 index 000000000..c04c83562 --- /dev/null +++ b/org.argeo.cms.ui.workbench/src/org/argeo/security/ui/admin/internal/commands/NewUser.java @@ -0,0 +1,328 @@ +/* + * 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.ui.admin.internal.commands; + +import java.util.Dictionary; +import java.util.List; +import java.util.Map; + +import javax.naming.InvalidNameException; +import javax.naming.ldap.LdapName; +import javax.naming.ldap.Rdn; + +import org.argeo.cms.CmsException; +import org.argeo.cms.util.useradmin.UserAdminUtils; +import org.argeo.eclipse.ui.EclipseUiUtils; +import org.argeo.eclipse.ui.dialogs.ErrorFeedback; +import org.argeo.jcr.ArgeoNames; +import org.argeo.osgi.useradmin.LdifName; +import org.argeo.osgi.useradmin.UserAdminConf; +import org.argeo.security.ui.admin.SecurityAdminPlugin; +import org.argeo.security.ui.admin.internal.UserAdminWrapper; +import org.eclipse.core.commands.AbstractHandler; +import org.eclipse.core.commands.ExecutionEvent; +import org.eclipse.core.commands.ExecutionException; +import org.eclipse.jface.wizard.Wizard; +import org.eclipse.jface.wizard.WizardDialog; +import org.eclipse.jface.wizard.WizardPage; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.ModifyEvent; +import org.eclipse.swt.events.ModifyListener; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Combo; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.Text; +import org.eclipse.ui.handlers.HandlerUtil; +import org.osgi.service.useradmin.Role; +import org.osgi.service.useradmin.User; +import org.osgi.service.useradmin.UserAdminEvent; + +/** Open a wizard that enables creation of a new user. */ +public class NewUser extends AbstractHandler { + /** + * Email addresses must match this regexp pattern ({@value #EMAIL_PATTERN}. + * Thanks to this tip. + */ + public final static String EMAIL_PATTERN = "^[_A-Za-z0-9-]+(\\.[_A-Za-z0-9-]+)*@[A-Za-z0-9-]+(\\.[A-Za-z0-9]+)*(\\.[A-Za-z]{2,})$"; + // private final static Log log = LogFactory.getLog(NewUser.class); + public final static String ID = SecurityAdminPlugin.PLUGIN_ID + ".newUser"; + + /* DEPENDENCY INJECTION */ + private UserAdminWrapper userAdminWrapper; + + public Object execute(ExecutionEvent event) throws ExecutionException { + NewUserWizard newUserWizard = new NewUserWizard(); + newUserWizard.setWindowTitle("User creation"); + WizardDialog dialog = new WizardDialog( + HandlerUtil.getActiveShell(event), newUserWizard); + dialog.open(); + return null; + } + + private class NewUserWizard extends Wizard { + + // pages + private MainUserInfoWizardPage mainUserInfo; + + // End user fields + private Text dNameTxt, usernameTxt, firstNameTxt, lastNameTxt, + primaryMailTxt, pwd1Txt, pwd2Txt; + private Combo baseDnCmb; + + public NewUserWizard() { + + } + + @Override + public void addPages() { + mainUserInfo = new MainUserInfoWizardPage(); + addPage(mainUserInfo); + String message = "Default wizard that also eases user creation tests:\n " + + "Mail and last name are automatically " + + "generated form the uid. Password are defauted to 'demo'."; + mainUserInfo.setMessage(message, WizardPage.WARNING); + } + + @SuppressWarnings({ "rawtypes", "unchecked" }) + @Override + public boolean performFinish() { + if (!canFinish()) + return false; + String username = mainUserInfo.getUsername(); + userAdminWrapper.beginTransactionIfNeeded(); + try { + User user = (User) userAdminWrapper.getUserAdmin().createRole( + getDn(username), Role.USER); + + Dictionary props = user.getProperties(); + + String lastNameStr = lastNameTxt.getText(); + if (EclipseUiUtils.notEmpty(lastNameStr)) + props.put(LdifName.sn.name(), lastNameStr); + + String firstNameStr = firstNameTxt.getText(); + if (EclipseUiUtils.notEmpty(firstNameStr)) + props.put(LdifName.givenName.name(), firstNameStr); + + String cn = UserAdminUtils.buildDefaultCn(firstNameStr, + lastNameStr); + if (EclipseUiUtils.notEmpty(cn)) + props.put(LdifName.cn.name(), cn); + + String mailStr = primaryMailTxt.getText(); + if (EclipseUiUtils.notEmpty(mailStr)) + props.put(LdifName.mail.name(), mailStr); + + char[] password = mainUserInfo.getPassword(); + user.getCredentials().put(null, password); + userAdminWrapper.commitOrNotifyTransactionStateChange(); + userAdminWrapper.notifyListeners(new UserAdminEvent(null, + UserAdminEvent.ROLE_CREATED, user)); + return true; + } catch (Exception e) { + ErrorFeedback.show("Cannot create new user " + username, e); + return false; + } + } + + private class MainUserInfoWizardPage extends WizardPage implements + ModifyListener, ArgeoNames { + private static final long serialVersionUID = -3150193365151601807L; + + public MainUserInfoWizardPage() { + super("Main"); + setTitle("Required Information"); + } + + @Override + public void createControl(Composite parent) { + Composite composite = new Composite(parent, SWT.NONE); + composite.setLayout(new GridLayout(2, false)); + dNameTxt = EclipseUiUtils.createGridLT(composite, + "Distinguished name", this); + dNameTxt.setEnabled(false); + + baseDnCmb = createGridLC(composite, "Base DN"); + initialiseDnCmb(baseDnCmb); + baseDnCmb.addModifyListener(this); + baseDnCmb.addModifyListener(new ModifyListener() { + private static final long serialVersionUID = -1435351236582736843L; + + @Override + public void modifyText(ModifyEvent event) { + String name = usernameTxt.getText(); + dNameTxt.setText(getDn(name)); + } + }); + + usernameTxt = EclipseUiUtils.createGridLT(composite, + "Local ID", this); + usernameTxt.addModifyListener(new ModifyListener() { + private static final long serialVersionUID = -1435351236582736843L; + + @Override + public void modifyText(ModifyEvent event) { + String name = usernameTxt.getText(); + if (name.trim().equals("")) { + dNameTxt.setText(""); + lastNameTxt.setText(""); + primaryMailTxt.setText(""); + pwd1Txt.setText(""); + pwd2Txt.setText(""); + } else { + dNameTxt.setText(getDn(name)); + lastNameTxt.setText(name.toUpperCase()); + primaryMailTxt.setText(getMail(name)); + pwd1Txt.setText("demo"); + pwd2Txt.setText("demo"); + } + } + }); + + primaryMailTxt = EclipseUiUtils.createGridLT(composite, + "Email", this); + firstNameTxt = EclipseUiUtils.createGridLT(composite, + "First name", this); + lastNameTxt = EclipseUiUtils.createGridLT(composite, + "Last name", this); + pwd1Txt = EclipseUiUtils.createGridLP(composite, "Password", + this); + pwd2Txt = EclipseUiUtils.createGridLP(composite, + "Repeat password", this); + setControl(composite); + + // Initialize buttons + setPageComplete(false); + getContainer().updateButtons(); + } + + @Override + public void modifyText(ModifyEvent event) { + String message = checkComplete(); + if (message != null) { + setMessage(message, WizardPage.ERROR); + setPageComplete(false); + } else { + setMessage("Complete", WizardPage.INFORMATION); + setPageComplete(true); + } + getContainer().updateButtons(); + } + + /** @return error message or null if complete */ + protected String checkComplete() { + String name = usernameTxt.getText(); + + if (name.trim().equals("")) + return "User name must not be empty"; + Role role = userAdminWrapper.getUserAdmin() + .getRole(getDn(name)); + if (role != null) + return "User " + name + " already exists"; + if (!primaryMailTxt.getText().matches(EMAIL_PATTERN)) + return "Not a valid email address"; + if (lastNameTxt.getText().trim().equals("")) + return "Specify a last name"; + if (pwd1Txt.getText().trim().equals("")) + return "Specify a password"; + if (pwd2Txt.getText().trim().equals("")) + return "Repeat the password"; + if (!pwd2Txt.getText().equals(pwd1Txt.getText())) + return "Passwords are different"; + return null; + } + + @Override + public void setVisible(boolean visible) { + super.setVisible(visible); + if (visible) + if (baseDnCmb.getSelectionIndex() == -1) + baseDnCmb.setFocus(); + else + usernameTxt.setFocus(); + } + + public String getUsername() { + return usernameTxt.getText(); + } + + public char[] getPassword() { + return pwd1Txt.getTextChars(); + } + + } + + private Map getDns() { + return userAdminWrapper.getKnownBaseDns(true); + } + + private String getDn(String uid) { + Map dns = getDns(); + String bdn = baseDnCmb.getText(); + if (EclipseUiUtils.notEmpty(bdn)) { + Dictionary props = UserAdminConf.uriAsProperties(dns + .get(bdn)); + String dn = LdifName.uid.name() + "=" + uid + "," + + UserAdminConf.userBase.getValue(props) + "," + bdn; + return dn; + } + return null; + } + + private void initialiseDnCmb(Combo combo) { + Map dns = userAdminWrapper.getKnownBaseDns(true); + if (dns.isEmpty()) + throw new CmsException( + "No writable base dn found. Cannot create user"); + combo.setItems(dns.keySet().toArray(new String[0])); + if (dns.size() == 1) + combo.select(0); + } + + private String getMail(String username) { + if (baseDnCmb.getSelectionIndex() == -1) + return null; + String baseDn = baseDnCmb.getText(); + try { + LdapName name = new LdapName(baseDn); + List rdns = name.getRdns(); + return username + "@" + (String) rdns.get(1).getValue() + '.' + + (String) rdns.get(0).getValue(); + } catch (InvalidNameException e) { + throw new CmsException("Unable to generate mail for " + + username + " with base dn " + baseDn, e); + } + } + } + + private Combo createGridLC(Composite parent, String label) { + Label lbl = new Label(parent, SWT.LEAD); + lbl.setText(label); + lbl.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false)); + Combo combo = new Combo(parent, SWT.LEAD | SWT.BORDER | SWT.READ_ONLY); + combo.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false)); + return combo; + } + + /* DEPENDENCY INJECTION */ + public void setUserAdminWrapper(UserAdminWrapper userAdminWrapper) { + this.userAdminWrapper = userAdminWrapper; + } +} \ No newline at end of file diff --git a/org.argeo.cms.ui.workbench/src/org/argeo/security/ui/admin/internal/commands/SaveArgeoUser.java b/org.argeo.cms.ui.workbench/src/org/argeo/security/ui/admin/internal/commands/SaveArgeoUser.java new file mode 100644 index 000000000..61d8a7daf --- /dev/null +++ b/org.argeo.cms.ui.workbench/src/org/argeo/security/ui/admin/internal/commands/SaveArgeoUser.java @@ -0,0 +1,47 @@ +/* + * 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.ui.admin.internal.commands; + +import org.argeo.security.ui.admin.SecurityAdminPlugin; +import org.eclipse.core.commands.AbstractHandler; +import org.eclipse.core.commands.ExecutionEvent; +import org.eclipse.core.commands.ExecutionException; +import org.eclipse.jface.dialogs.MessageDialog; +import org.eclipse.swt.widgets.Display; +import org.eclipse.ui.IEditorPart; +import org.eclipse.ui.IWorkbenchPart; +import org.eclipse.ui.handlers.HandlerUtil; + +/** Save the currently edited Argeo user. */ +public class SaveArgeoUser extends AbstractHandler { + public final static String ID = SecurityAdminPlugin.PLUGIN_ID + + ".saveArgeoUser"; + + public Object execute(ExecutionEvent event) throws ExecutionException { + try { + IWorkbenchPart iwp = HandlerUtil.getActiveWorkbenchWindow(event) + .getActivePage().getActivePart(); + if (!(iwp instanceof IEditorPart)) + return null; + IEditorPart editor = (IEditorPart) iwp; + editor.doSave(null); + } catch (Exception e) { + MessageDialog.openError(Display.getDefault().getActiveShell(), + "Error", "Cannot save user: " + e.getMessage()); + } + return null; + } +} \ No newline at end of file diff --git a/org.argeo.cms.ui.workbench/src/org/argeo/security/ui/admin/internal/commands/UserBatchUpdate.java b/org.argeo.cms.ui.workbench/src/org/argeo/security/ui/admin/internal/commands/UserBatchUpdate.java new file mode 100644 index 000000000..c02f5c220 --- /dev/null +++ b/org.argeo.cms.ui.workbench/src/org/argeo/security/ui/admin/internal/commands/UserBatchUpdate.java @@ -0,0 +1,44 @@ +/* + * 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.ui.admin.internal.commands; + +import org.argeo.security.ui.admin.internal.UserAdminWrapper; +import org.argeo.security.ui.admin.internal.parts.UserBatchUpdateWizard; +import org.eclipse.core.commands.AbstractHandler; +import org.eclipse.core.commands.ExecutionEvent; +import org.eclipse.core.commands.ExecutionException; +import org.eclipse.jface.wizard.WizardDialog; +import org.eclipse.ui.handlers.HandlerUtil; + +/** Launch a wizard to perform batch process on users */ +public class UserBatchUpdate extends AbstractHandler { + + /* DEPENDENCY INJECTION */ + private UserAdminWrapper uaWrapper; + + public Object execute(ExecutionEvent event) throws ExecutionException { + UserBatchUpdateWizard wizard = new UserBatchUpdateWizard(uaWrapper); + wizard.setWindowTitle("User batch processing"); + WizardDialog dialog = new WizardDialog( + HandlerUtil.getActiveShell(event), wizard); + dialog.open(); + return null; + } + + public void setUserAdminWrapper(UserAdminWrapper userAdminWrapper) { + this.uaWrapper = userAdminWrapper; + } +} \ No newline at end of file diff --git a/org.argeo.cms.ui.workbench/src/org/argeo/security/ui/admin/internal/commands/UserTransactionHandler.java b/org.argeo.cms.ui.workbench/src/org/argeo/security/ui/admin/internal/commands/UserTransactionHandler.java new file mode 100644 index 000000000..236584ca3 --- /dev/null +++ b/org.argeo.cms.ui.workbench/src/org/argeo/security/ui/admin/internal/commands/UserTransactionHandler.java @@ -0,0 +1,93 @@ +/* + * 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.ui.admin.internal.commands; + +import javax.transaction.Status; +import javax.transaction.UserTransaction; + +import org.argeo.cms.CmsException; +import org.argeo.security.ui.admin.SecurityAdminPlugin; +import org.argeo.security.ui.admin.internal.UiAdminUtils; +import org.argeo.security.ui.admin.internal.UserAdminWrapper; +import org.eclipse.core.commands.AbstractHandler; +import org.eclipse.core.commands.ExecutionEvent; +import org.eclipse.core.commands.ExecutionException; +import org.osgi.service.useradmin.UserAdminEvent; + +/** Manage the transaction that is bound to the current perspective */ +public class UserTransactionHandler extends AbstractHandler { + public final static String ID = SecurityAdminPlugin.PLUGIN_ID + + ".userTransactionHandler"; + + public final static String PARAM_COMMAND_ID = "param.commandId"; + + public final static String TRANSACTION_BEGIN = "transaction.begin"; + public final static String TRANSACTION_COMMIT = "transaction.commit"; + public final static String TRANSACTION_ROLLBACK = "transaction.rollback"; + + /* DEPENDENCY INJECTION */ + private UserAdminWrapper userAdminWrapper; + + public Object execute(ExecutionEvent event) throws ExecutionException { + String commandId = event.getParameter(PARAM_COMMAND_ID); + final UserTransaction userTransaction = userAdminWrapper + .getUserTransaction(); + try { + if (TRANSACTION_BEGIN.equals(commandId)) { + if (userTransaction.getStatus() != Status.STATUS_NO_TRANSACTION) + throw new CmsException("A transaction already exists"); + else + userTransaction.begin(); + } else if (TRANSACTION_COMMIT.equals(commandId)) { + if (userTransaction.getStatus() == Status.STATUS_NO_TRANSACTION) + throw new CmsException("No transaction."); + else + userTransaction.commit(); + } else if (TRANSACTION_ROLLBACK.equals(commandId)) { + if (userTransaction.getStatus() == Status.STATUS_NO_TRANSACTION) + throw new CmsException("No transaction to rollback."); + else { + userTransaction.rollback(); + userAdminWrapper.notifyListeners(new UserAdminEvent(null, + UserAdminEvent.ROLE_CHANGED, null)); + } + } + + UiAdminUtils.notifyTransactionStateChange(userTransaction); + // Try to remove invalid thread access errors when managing users. + // HandlerUtil.getActivePart(event).getSite().getShell().getDisplay() + // .asyncExec(new Runnable() { + // @Override + // public void run() { + // UiAdminUtils + // .notifyTransactionStateChange(userTransaction); + // } + // }); + + } catch (CmsException e) { + throw e; + } catch (Exception e) { + throw new CmsException("Unable to call " + commandId + " on " + + userTransaction, e); + } + return null; + } + + /* DEPENDENCY INJECTION */ + public void setUserAdminWrapper(UserAdminWrapper userAdminWrapper) { + this.userAdminWrapper = userAdminWrapper; + } +} \ No newline at end of file diff --git a/org.argeo.cms.ui.workbench/src/org/argeo/security/ui/admin/internal/parts/ArgeoUserEditorInput.java b/org.argeo.cms.ui.workbench/src/org/argeo/security/ui/admin/internal/parts/ArgeoUserEditorInput.java new file mode 100644 index 000000000..cca2a1215 --- /dev/null +++ b/org.argeo.cms.ui.workbench/src/org/argeo/security/ui/admin/internal/parts/ArgeoUserEditorInput.java @@ -0,0 +1,65 @@ +/* + * 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.ui.admin.internal.parts; + +import org.eclipse.jface.resource.ImageDescriptor; +import org.eclipse.ui.IEditorInput; +import org.eclipse.ui.IPersistableElement; + +/** Editor input for an Argeo user. */ +public class ArgeoUserEditorInput implements IEditorInput { + private final String username; + + public ArgeoUserEditorInput(String username) { + this.username = username; + } + + public Object getAdapter(@SuppressWarnings("rawtypes") Class adapter) { + return null; + } + + public boolean exists() { + return username != null; + } + + public ImageDescriptor getImageDescriptor() { + return null; + } + + public String getName() { + return username != null ? username : ""; + } + + public IPersistableElement getPersistable() { + return null; + } + + public String getToolTipText() { + return username != null ? username : ""; + } + + public boolean equals(Object obj) { + if (!(obj instanceof ArgeoUserEditorInput)) + return false; + if (((ArgeoUserEditorInput) obj).getUsername() == null) + return false; + return ((ArgeoUserEditorInput) obj).getUsername().equals(username); + } + + public String getUsername() { + return username; + } +} diff --git a/org.argeo.cms.ui.workbench/src/org/argeo/security/ui/admin/internal/parts/DefaultUserMainPage.java b/org.argeo.cms.ui.workbench/src/org/argeo/security/ui/admin/internal/parts/DefaultUserMainPage.java new file mode 100644 index 000000000..6529efe30 --- /dev/null +++ b/org.argeo.cms.ui.workbench/src/org/argeo/security/ui/admin/internal/parts/DefaultUserMainPage.java @@ -0,0 +1,243 @@ +/* + * 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.ui.admin.internal.parts; + +import java.util.Arrays; + +import javax.jcr.Node; +import javax.jcr.Property; +import javax.jcr.RepositoryException; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.argeo.cms.CmsException; +import org.argeo.jcr.ArgeoNames; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.ModifyEvent; +import org.eclipse.swt.events.ModifyListener; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.Text; +import org.eclipse.ui.forms.AbstractFormPart; +import org.eclipse.ui.forms.IManagedForm; +import org.eclipse.ui.forms.SectionPart; +import org.eclipse.ui.forms.editor.FormEditor; +import org.eclipse.ui.forms.editor.FormPage; +import org.eclipse.ui.forms.widgets.FormToolkit; +import org.eclipse.ui.forms.widgets.ScrolledForm; +import org.eclipse.ui.forms.widgets.Section; + +/** Display/edit the properties common to all Argeo users */ +public class DefaultUserMainPage extends FormPage implements ArgeoNames { + final static String ID = "argeoUserEditor.mainPage"; + + private final static Log log = LogFactory.getLog(DefaultUserMainPage.class); + private Node userProfile; + + private char[] newPassword; + + public DefaultUserMainPage(FormEditor editor, Node userProfile) { + super(editor, ID, "Main"); + this.userProfile = userProfile; + } + + protected void createFormContent(final IManagedForm mf) { + try { + ScrolledForm form = mf.getForm(); + refreshFormTitle(form); + GridLayout mainLayout = new GridLayout(1, true); + form.getBody().setLayout(mainLayout); + + createGeneralPart(form.getBody()); + createPassworPart(form.getBody()); + } catch (RepositoryException e) { + throw new CmsException("Cannot create form content", e); + } + } + + /** Creates the general section */ + protected void createGeneralPart(Composite parent) + throws RepositoryException { + FormToolkit tk = getManagedForm().getToolkit(); + Section section = tk.createSection(parent, Section.TITLE_BAR); + section.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false)); + section.setText("General"); + Composite body = tk.createComposite(section, SWT.WRAP); + section.setClient(body); + GridLayout layout = new GridLayout(2, false); + body.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false)); + body.setLayout(layout); + + final Text commonName = createLT(body, "Displayed Name", + getProperty(Property.JCR_TITLE)); + final Text firstName = createLT(body, "First name", + getProperty(ARGEO_FIRST_NAME)); + final Text lastName = createLT(body, "Last name", + getProperty(ARGEO_LAST_NAME)); + final Text email = createLT(body, "Email", + getProperty(ARGEO_PRIMARY_EMAIL)); + final Text description = createLMT(body, "Description", + getProperty(Property.JCR_DESCRIPTION)); + + // create form part (controller) + AbstractFormPart part = new SectionPart(section) { + public void commit(boolean onSave) { + try { + userProfile.getSession().getWorkspace().getVersionManager() + .checkout(userProfile.getPath()); + userProfile.setProperty(Property.JCR_TITLE, + commonName.getText()); + userProfile.setProperty(ARGEO_FIRST_NAME, + firstName.getText()); + userProfile + .setProperty(ARGEO_LAST_NAME, lastName.getText()); + userProfile.setProperty(ARGEO_PRIMARY_EMAIL, + email.getText()); + userProfile.setProperty(Property.JCR_DESCRIPTION, + description.getText()); + userProfile.getSession().save(); + userProfile.getSession().getWorkspace().getVersionManager() + .checkin(userProfile.getPath()); + super.commit(onSave); + refreshFormTitle(getManagedForm().getForm()); + if (log.isTraceEnabled()) + log.trace("General part committed"); + } catch (RepositoryException e) { + throw new CmsException("Cannot commit", e); + } + } + }; + // if (username != null) + // username.addModifyListener(new FormPartML(part)); + commonName.addModifyListener(new FormPartML(part)); + firstName.addModifyListener(new FormPartML(part)); + lastName.addModifyListener(new FormPartML(part)); + email.addModifyListener(new FormPartML(part)); + description.addModifyListener(new FormPartML(part)); + getManagedForm().addPart(part); + } + + private void refreshFormTitle(ScrolledForm form) throws RepositoryException { + form.setText(getProperty(Property.JCR_TITLE) + + (userProfile.getProperty(ARGEO_ENABLED).getBoolean() ? "" + : " [DISABLED]")); + } + + /** @return the property, or the empty string if not set */ + protected String getProperty(String name) throws RepositoryException { + return userProfile.hasProperty(name) ? userProfile.getProperty(name) + .getString() : ""; + } + + /** Creates the password section */ + protected void createPassworPart(Composite parent) { + FormToolkit tk = getManagedForm().getToolkit(); + Section section = tk.createSection(parent, Section.TITLE_BAR); + section.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false)); + section.setText("Password"); + + Composite body = tk.createComposite(section, SWT.WRAP); + section.setClient(body); + GridLayout layout = new GridLayout(2, false); + body.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false)); + body.setLayout(layout); + + // add widgets (view) + final Text password1 = createLP(body, "New password", ""); + final Text password2 = createLP(body, "Repeat password", ""); + // create form part (controller) + AbstractFormPart part = new SectionPart(section) { + + public void commit(boolean onSave) { + if (!password1.getText().equals("") + || !password2.getText().equals("")) { + if (password1.getText().equals(password2.getText())) { + newPassword = password1.getText().toCharArray(); + password1.setText(""); + password2.setText(""); + super.commit(onSave); + } else { + password1.setText(""); + password2.setText(""); + throw new CmsException("Passwords are not equals"); + } + } + } + + }; + password1.addModifyListener(new FormPartML(part)); + password2.addModifyListener(new FormPartML(part)); + getManagedForm().addPart(part); + } + + /** Creates label and text. */ + protected Text createLT(Composite body, String label, String value) { + FormToolkit toolkit = getManagedForm().getToolkit(); + Label lbl = toolkit.createLabel(body, label); + lbl.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false)); + Text text = toolkit.createText(body, value, SWT.BORDER); + text.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false)); + return text; + } + + /** Creates label and multiline text. */ + protected Text createLMT(Composite body, String label, String value) { + FormToolkit toolkit = getManagedForm().getToolkit(); + Label lbl = toolkit.createLabel(body, label); + lbl.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false)); + Text text = toolkit.createText(body, value, SWT.BORDER | SWT.MULTI); + text.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, true)); + return text; + } + + /** Creates label and password. */ + protected Text createLP(Composite body, String label, String value) { + FormToolkit toolkit = getManagedForm().getToolkit(); + Label lbl = toolkit.createLabel(body, label); + lbl.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false)); + Text text = toolkit.createText(body, value, SWT.BORDER | SWT.PASSWORD); + text.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false)); + return text; + } + + private class FormPartML implements ModifyListener { + private static final long serialVersionUID = 6299808129505381333L; + private AbstractFormPart formPart; + + public FormPartML(AbstractFormPart generalPart) { + this.formPart = generalPart; + } + + public void modifyText(ModifyEvent e) { + formPart.markDirty(); + } + } + + public String getNewPassword() { + if (newPassword != null) + return new String(newPassword); + else + return null; + } + + public void resetNewPassword() { + if (newPassword != null) + Arrays.fill(newPassword, 'x'); + newPassword = null; + } +} diff --git a/org.argeo.cms.ui.workbench/src/org/argeo/security/ui/admin/internal/parts/GroupMainPage.java b/org.argeo.cms.ui.workbench/src/org/argeo/security/ui/admin/internal/parts/GroupMainPage.java new file mode 100644 index 000000000..fe56679db --- /dev/null +++ b/org.argeo.cms.ui.workbench/src/org/argeo/security/ui/admin/internal/parts/GroupMainPage.java @@ -0,0 +1,477 @@ +/* + * 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.ui.admin.internal.parts; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +import javax.transaction.UserTransaction; + +import org.argeo.cms.CmsException; +import org.argeo.cms.util.useradmin.UserAdminUtils; +import org.argeo.eclipse.ui.ColumnDefinition; +import org.argeo.eclipse.ui.EclipseUiUtils; +import org.argeo.eclipse.ui.parts.LdifUsersTable; +import org.argeo.jcr.ArgeoNames; +import org.argeo.osgi.useradmin.LdifName; +import org.argeo.security.ui.admin.SecurityAdminImages; +import org.argeo.security.ui.admin.internal.UserAdminWrapper; +import org.argeo.security.ui.admin.internal.parts.UserEditor.GroupChangeListener; +import org.argeo.security.ui.admin.internal.parts.UserEditor.MainInfoListener; +import org.argeo.security.ui.admin.internal.providers.CommonNameLP; +import org.argeo.security.ui.admin.internal.providers.MailLP; +import org.argeo.security.ui.admin.internal.providers.RoleIconLP; +import org.argeo.security.ui.admin.internal.providers.UserFilter; +import org.argeo.security.ui.admin.internal.providers.UserNameLP; +import org.argeo.security.ui.admin.internal.providers.UserTableDefaultDClickListener; +import org.eclipse.jface.action.Action; +import org.eclipse.jface.action.ToolBarManager; +import org.eclipse.jface.dialogs.MessageDialog; +import org.eclipse.jface.resource.ImageDescriptor; +import org.eclipse.jface.viewers.ISelection; +import org.eclipse.jface.viewers.IStructuredSelection; +import org.eclipse.jface.viewers.TableViewer; +import org.eclipse.jface.viewers.ViewerDropAdapter; +import org.eclipse.swt.SWT; +import org.eclipse.swt.dnd.DND; +import org.eclipse.swt.dnd.DropTargetEvent; +import org.eclipse.swt.dnd.TextTransfer; +import org.eclipse.swt.dnd.Transfer; +import org.eclipse.swt.dnd.TransferData; +import org.eclipse.swt.events.DisposeEvent; +import org.eclipse.swt.events.DisposeListener; +import org.eclipse.swt.events.ModifyListener; +import org.eclipse.swt.graphics.Cursor; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.Shell; +import org.eclipse.swt.widgets.Text; +import org.eclipse.swt.widgets.ToolBar; +import org.eclipse.ui.forms.AbstractFormPart; +import org.eclipse.ui.forms.IManagedForm; +import org.eclipse.ui.forms.SectionPart; +import org.eclipse.ui.forms.editor.FormEditor; +import org.eclipse.ui.forms.editor.FormPage; +import org.eclipse.ui.forms.widgets.FormToolkit; +import org.eclipse.ui.forms.widgets.ScrolledForm; +import org.eclipse.ui.forms.widgets.Section; +import org.osgi.service.useradmin.Group; +import org.osgi.service.useradmin.Role; +import org.osgi.service.useradmin.User; +import org.osgi.service.useradmin.UserAdmin; +import org.osgi.service.useradmin.UserAdminEvent; + +/** Display/edit main properties of a given group */ +public class GroupMainPage extends FormPage implements ArgeoNames { + final static String ID = "GroupEditor.mainPage"; + + private final UserEditor editor; + private UserAdminWrapper userAdminWrapper; + + // Local configuration + private final int PRE_TITLE_INDENT = 10; + + public GroupMainPage(FormEditor editor, UserAdminWrapper userAdminWrapper) { + super(editor, ID, "Main"); + this.editor = (UserEditor) editor; + this.userAdminWrapper = userAdminWrapper; + } + + protected void createFormContent(final IManagedForm mf) { + ScrolledForm form = mf.getForm(); + Composite body = form.getBody(); + GridLayout mainLayout = new GridLayout(); + body.setLayout(mainLayout); + Group group = (Group) editor.getDisplayedUser(); + appendOverviewPart(body, group); + appendMembersPart(body, group); + } + + /** Creates the general section */ + protected void appendOverviewPart(final Composite parent, final Group group) { + FormToolkit tk = getManagedForm().getToolkit(); + Composite body = addSection(tk, parent, "Main information"); + GridLayout layout = new GridLayout(2, false); + body.setLayout(layout); + + final Text dnTxt = createLT(body, "DN", group.getName()); + dnTxt.setEnabled(false); + + final Text cnTxt = createLT(body, "Common Name", + UserAdminUtils.getProperty(group, LdifName.cn.name())); + cnTxt.setEnabled(false); + + Label descLbl = new Label(body, SWT.LEAD); + descLbl.setText("Description"); + descLbl.setLayoutData(new GridData(SWT.RIGHT, SWT.TOP, false, false)); + final Text descTxt = new Text(body, SWT.LEAD | SWT.MULTI | SWT.WRAP + | SWT.BORDER); + GridData gd = EclipseUiUtils.fillAll(); + gd.heightHint = 100; + descTxt.setLayoutData(gd); + + // create form part (controller) + AbstractFormPart part = new SectionPart((Section) body.getParent()) { + + private MainInfoListener listener; + + @Override + public void initialize(IManagedForm form) { + super.initialize(form); + listener = editor.new MainInfoListener(parent.getDisplay(), + this); + userAdminWrapper.addListener(listener); + } + + @Override + public void dispose() { + userAdminWrapper.removeListener(listener); + super.dispose(); + } + + @SuppressWarnings("unchecked") + public void commit(boolean onSave) { + group.getProperties().put(LdifName.description.name(), + descTxt.getText()); + // Enable common name ? + // editor.setProperty(UserAdminConstants.KEY_CN, + // email.getText()); + super.commit(onSave); + } + + @Override + public void refresh() { + refreshFormTitle(group); + dnTxt.setText(group.getName()); + cnTxt.setText(UserAdminUtils.getProperty(group, + LdifName.cn.name())); + descTxt.setText(UserAdminUtils.getProperty(group, + LdifName.description.name())); + super.refresh(); + } + }; + + ModifyListener defaultListener = editor.new FormPartML(part); + descTxt.addModifyListener(defaultListener); + getManagedForm().addPart(part); + } + + /** Filtered table with members. Has drag & drop ability */ + protected void appendMembersPart(Composite parent, Group group) { + + FormToolkit tk = getManagedForm().getToolkit(); + Section section = tk.createSection(parent, Section.TITLE_BAR); + section.setLayoutData(EclipseUiUtils.fillAll()); + + Composite body = new Composite(section, SWT.NO_FOCUS); + section.setClient(body); + body.setLayoutData(EclipseUiUtils.fillAll()); + + LdifUsersTable userTableViewerCmp = createMemberPart(body, group); + + SectionPart part = new GroupMembersPart(section, userTableViewerCmp, + group); + getManagedForm().addPart(part); + addRemoveAbitily(part, userTableViewerCmp.getTableViewer(), group); + } + + public LdifUsersTable createMemberPart(Composite parent, Group group) { + parent.setLayout(EclipseUiUtils.noSpaceGridLayout()); + + // Define the displayed columns + List columnDefs = new ArrayList(); + columnDefs.add(new ColumnDefinition(new RoleIconLP(), "", 0, 24)); + columnDefs.add(new ColumnDefinition(new CommonNameLP(), "Common Name", + 150)); + columnDefs.add(new ColumnDefinition(new MailLP(), "Primary Mail", 150)); + columnDefs.add(new ColumnDefinition(new UserNameLP(), + "Distinguished Name", 240)); + + // Create and configure the table + LdifUsersTable userViewerCmp = new MyUserTableViewer(parent, SWT.MULTI + | SWT.H_SCROLL | SWT.V_SCROLL, userAdminWrapper.getUserAdmin()); + + userViewerCmp.setColumnDefinitions(columnDefs); + userViewerCmp.populate(true, false); + userViewerCmp.setLayoutData(EclipseUiUtils.fillAll()); + + // Controllers + TableViewer userViewer = userViewerCmp.getTableViewer(); + userViewer.addDoubleClickListener(new UserTableDefaultDClickListener()); + int operations = DND.DROP_COPY | DND.DROP_MOVE; + Transfer[] tt = new Transfer[] { TextTransfer.getInstance() }; + userViewer.addDropSupport(operations, tt, + new GroupDropListener(userAdminWrapper, userViewerCmp, + (Group) editor.getDisplayedUser())); + + return userViewerCmp; + } + + // Local viewers + private class MyUserTableViewer extends LdifUsersTable { + private static final long serialVersionUID = 8467999509931900367L; + + private final UserFilter userFilter; + + public MyUserTableViewer(Composite parent, int style, + UserAdmin userAdmin) { + super(parent, style, true); + userFilter = new UserFilter(); + + } + + @Override + protected List listFilteredElements(String filter) { + Group group = (Group) editor.getDisplayedUser(); + Role[] roles = group.getMembers(); + List users = new ArrayList(); + userFilter.setSearchText(filter); + for (Role role : roles) + // if (role.getType() == Role.GROUP) + if (userFilter.select(null, null, role)) + users.add((User) role); + return users; + } + } + + private void addRemoveAbitily(SectionPart sectionPart, + TableViewer userViewer, Group group) { + Section section = sectionPart.getSection(); + ToolBarManager toolBarManager = new ToolBarManager(SWT.FLAT); + ToolBar toolbar = toolBarManager.createControl(section); + final Cursor handCursor = new Cursor(section.getDisplay(), + SWT.CURSOR_HAND); + toolbar.setCursor(handCursor); + toolbar.addDisposeListener(new DisposeListener() { + private static final long serialVersionUID = 3882131405820522925L; + + public void widgetDisposed(DisposeEvent e) { + if ((handCursor != null) && (handCursor.isDisposed() == false)) { + handCursor.dispose(); + } + } + }); + + Action action = new RemoveMembershipAction(userViewer, group, + "Remove selected items from this group", + SecurityAdminImages.ICON_REMOVE_DESC); + toolBarManager.add(action); + toolBarManager.update(true); + section.setTextClient(toolbar); + } + + private class RemoveMembershipAction extends Action { + private static final long serialVersionUID = -1337713097184522588L; + + private final TableViewer userViewer; + private final Group group; + + RemoveMembershipAction(TableViewer userViewer, Group group, + String name, ImageDescriptor img) { + super(name, img); + this.userViewer = userViewer; + this.group = group; + } + + @Override + public void run() { + ISelection selection = userViewer.getSelection(); + if (selection.isEmpty()) + return; + + @SuppressWarnings("unchecked") + Iterator it = ((IStructuredSelection) selection).iterator(); + List users = new ArrayList(); + while (it.hasNext()) { + User currUser = it.next(); + users.add(currUser); + } + + userAdminWrapper.beginTransactionIfNeeded(); + for (User user : users) { + group.removeMember(user); + } + userAdminWrapper.commitOrNotifyTransactionStateChange(); + userAdminWrapper.notifyListeners(new UserAdminEvent(null, + UserAdminEvent.ROLE_CHANGED, group)); + } + } + + // LOCAL CONTROLLERS + private class GroupMembersPart extends SectionPart { + private final LdifUsersTable userViewer; + private final Group group; + + private GroupChangeListener listener; + + public GroupMembersPart(Section section, LdifUsersTable userViewer, + Group group) { + super(section); + this.userViewer = userViewer; + this.group = group; + } + + @Override + public void initialize(IManagedForm form) { + super.initialize(form); + listener = editor.new GroupChangeListener(userViewer.getDisplay(), + GroupMembersPart.this); + userAdminWrapper.addListener(listener); + } + + @Override + public void dispose() { + userAdminWrapper.removeListener(listener); + super.dispose(); + } + + @Override + public void refresh() { + refreshFormTitle(group); + getSection().setText( + "Members of group " + + UserAdminUtils.getProperty(group, + LdifName.cn.name())); + userViewer.refresh(); + super.refresh(); + } + } + + /** + * Defines this table as being a potential target to add group membership + * (roles) to this group + */ + private class GroupDropListener extends ViewerDropAdapter { + private static final long serialVersionUID = 2893468717831451621L; + + private final UserAdminWrapper userAdminWrapper; + // private final LdifUsersTable myUserViewerCmp; + private final Group myGroup; + + public GroupDropListener(UserAdminWrapper userAdminWrapper, + LdifUsersTable userTableViewerCmp, Group group) { + super(userTableViewerCmp.getTableViewer()); + this.userAdminWrapper = userAdminWrapper; + this.myGroup = group; + // this.myUserViewerCmp = userTableViewerCmp; + } + + @Override + public boolean validateDrop(Object target, int operation, + TransferData transferType) { + // Target is always OK in a list only view + // TODO check if not a string + boolean validDrop = true; + return validDrop; + } + + @Override + public void drop(DropTargetEvent event) { + // TODO Is there an opportunity to perform the check before? + + String newUserName = (String) event.data; + UserAdmin myUserAdmin = userAdminWrapper.getUserAdmin(); + Role role = myUserAdmin.getRole(newUserName); + if (role.getType() == Role.GROUP) { + Group newGroup = (Group) role; + Shell shell = getViewer().getControl().getShell(); + // Sanity checks + if (myGroup == newGroup) { // Equality + MessageDialog.openError(shell, "Forbidden addition ", + "A group cannot be a member of itself."); + return; + } + + // Cycle + String myName = myGroup.getName(); + List myMemberships = editor.getFlatGroups(myGroup); + if (myMemberships.contains(newGroup)) { + MessageDialog.openError(shell, "Forbidden addition: cycle", + "Cannot add " + newUserName + " to group " + myName + + ". This would create a cycle"); + return; + } + + // Already member + List newGroupMemberships = editor.getFlatGroups(newGroup); + if (newGroupMemberships.contains(myGroup)) { + MessageDialog.openError(shell, "Forbidden addition", + "Cannot add " + newUserName + " to group " + myName + + ", this membership already exists"); + return; + } + userAdminWrapper.beginTransactionIfNeeded(); + myGroup.addMember(newGroup); + userAdminWrapper.commitOrNotifyTransactionStateChange(); + userAdminWrapper.notifyListeners(new UserAdminEvent(null, + UserAdminEvent.ROLE_CHANGED, myGroup)); + } else if (role.getType() == Role.USER) { + // TODO check if the group is already member of this group + UserTransaction transaction = userAdminWrapper + .beginTransactionIfNeeded(); + User user = (User) role; + myGroup.addMember(user); + if (UserAdminWrapper.COMMIT_ON_SAVE) + try { + transaction.commit(); + } catch (Exception e) { + throw new CmsException("Cannot commit transaction " + + "after user group membership update", e); + } + userAdminWrapper.notifyListeners(new UserAdminEvent(null, + UserAdminEvent.ROLE_CHANGED, myGroup)); + } + super.drop(event); + } + + @Override + public boolean performDrop(Object data) { + // myUserViewerCmp.refresh(); + return true; + } + } + + // LOCAL HELPERS + private void refreshFormTitle(Group group) { + getManagedForm().getForm().setText( + UserAdminUtils.getProperty(group, LdifName.cn.name())); + } + + private Composite addSection(FormToolkit tk, Composite parent, String title) { + Section section = tk.createSection(parent, Section.TITLE_BAR); + GridData gd = EclipseUiUtils.fillWidth(); + gd.verticalAlignment = PRE_TITLE_INDENT; + section.setLayoutData(gd); + section.setText(title); + Composite body = tk.createComposite(section, SWT.WRAP); + body.setLayoutData(EclipseUiUtils.fillAll()); + section.setClient(body); + return body; + } + + /** Creates label and text. */ + private Text createLT(Composite body, String label, String value) { + FormToolkit toolkit = getManagedForm().getToolkit(); + Label lbl = toolkit.createLabel(body, label); + lbl.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false)); + Text text = toolkit.createText(body, value, SWT.BORDER); + text.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false)); + return text; + } +} \ No newline at end of file diff --git a/org.argeo.cms.ui.workbench/src/org/argeo/security/ui/admin/internal/parts/GroupsView.java b/org.argeo.cms.ui.workbench/src/org/argeo/security/ui/admin/internal/parts/GroupsView.java new file mode 100644 index 000000000..82f4e1bdd --- /dev/null +++ b/org.argeo.cms.ui.workbench/src/org/argeo/security/ui/admin/internal/parts/GroupsView.java @@ -0,0 +1,253 @@ +/* + * 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.ui.admin.internal.parts; + +import java.util.ArrayList; +import java.util.List; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.argeo.cms.CmsException; +import org.argeo.cms.auth.AuthConstants; +import org.argeo.cms.util.useradmin.UserAdminUtils; +import org.argeo.eclipse.ui.ColumnDefinition; +import org.argeo.eclipse.ui.EclipseUiUtils; +import org.argeo.eclipse.ui.parts.LdifUsersTable; +import org.argeo.jcr.ArgeoNames; +import org.argeo.osgi.useradmin.LdifName; +import org.argeo.security.ui.admin.SecurityAdminPlugin; +import org.argeo.security.ui.admin.internal.UiUserAdminListener; +import org.argeo.security.ui.admin.internal.UserAdminWrapper; +import org.argeo.security.ui.admin.internal.providers.CommonNameLP; +import org.argeo.security.ui.admin.internal.providers.DomainNameLP; +import org.argeo.security.ui.admin.internal.providers.RoleIconLP; +import org.argeo.security.ui.admin.internal.providers.UserDragListener; +import org.argeo.security.ui.admin.internal.providers.UserNameLP; +import org.argeo.security.ui.admin.internal.providers.UserTableDefaultDClickListener; +import org.eclipse.jface.viewers.TableViewer; +import org.eclipse.swt.SWT; +import org.eclipse.swt.dnd.DND; +import org.eclipse.swt.dnd.TextTransfer; +import org.eclipse.swt.dnd.Transfer; +import org.eclipse.swt.events.SelectionAdapter; +import org.eclipse.swt.events.SelectionEvent; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Button; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Display; +import org.eclipse.ui.part.ViewPart; +import org.osgi.framework.InvalidSyntaxException; +import org.osgi.service.useradmin.Role; +import org.osgi.service.useradmin.User; +import org.osgi.service.useradmin.UserAdminEvent; +import org.osgi.service.useradmin.UserAdminListener; + +/** List all groups with filter */ +public class GroupsView extends ViewPart implements ArgeoNames { + private final static Log log = LogFactory.getLog(GroupsView.class); + public final static String ID = SecurityAdminPlugin.PLUGIN_ID + + ".groupsView"; + + /* DEPENDENCY INJECTION */ + private UserAdminWrapper userAdminWrapper; + + // UI Objects + private LdifUsersTable groupTableViewerCmp; + private TableViewer userViewer; + private List columnDefs = new ArrayList(); + + private UserAdminListener listener; + + @Override + public void createPartControl(Composite parent) { + parent.setLayout(EclipseUiUtils.noSpaceGridLayout()); + + boolean isAdmin = UserAdminUtils.isUserInRole(AuthConstants.ROLE_ADMIN); + + // Define the displayed columns + columnDefs.add(new ColumnDefinition(new RoleIconLP(), "", 26)); + columnDefs.add(new ColumnDefinition(new CommonNameLP(), "Common Name", + 150)); + columnDefs.add(new ColumnDefinition(new DomainNameLP(), "Domain", 200)); + // Only show technical DN to admin + if (isAdmin) + columnDefs.add(new ColumnDefinition(new UserNameLP(), + "Distinguished Name", 300)); + + // Create and configure the table + groupTableViewerCmp = new MyUserTableViewer(parent, SWT.MULTI + | SWT.H_SCROLL | SWT.V_SCROLL); + + groupTableViewerCmp.setColumnDefinitions(columnDefs); + if (isAdmin) + groupTableViewerCmp.populateWithStaticFilters(false, false); + else + groupTableViewerCmp.populate(true, false); + + groupTableViewerCmp.setLayoutData(EclipseUiUtils.fillAll()); + + // Links + userViewer = groupTableViewerCmp.getTableViewer(); + userViewer.addDoubleClickListener(new UserTableDefaultDClickListener()); + getViewSite().setSelectionProvider(userViewer); + + // Really? + groupTableViewerCmp.refresh(); + + // Drag and drop + int operations = DND.DROP_COPY | DND.DROP_MOVE; + Transfer[] tt = new Transfer[] { TextTransfer.getInstance() }; + userViewer.addDragSupport(operations, tt, new UserDragListener( + userViewer)); + + // // Register a useradmin listener + // listener = new UserAdminListener() { + // @Override + // public void roleChanged(UserAdminEvent event) { + // if (userViewer != null && !userViewer.getTable().isDisposed()) + // refresh(); + // } + // }; + // userAdminWrapper.addListener(listener); + // } + + // Register a useradmin listener + listener = new MyUiUAListener(parent.getDisplay()); + userAdminWrapper.addListener(listener); + } + + private class MyUiUAListener extends UiUserAdminListener { + public MyUiUAListener(Display display) { + super(display); + } + + @Override + public void roleChangedToUiThread(UserAdminEvent event) { + if (userViewer != null && !userViewer.getTable().isDisposed()) + refresh(); + } + } + + private class MyUserTableViewer extends LdifUsersTable { + private static final long serialVersionUID = 8467999509931900367L; + + private boolean showSystemRoles = false; + + private final String[] knownProps = { LdifName.uid.name(), + LdifName.cn.name(), LdifName.dn.name() }; + + public MyUserTableViewer(Composite parent, int style) { + super(parent, style); + } + + protected void populateStaticFilters(Composite staticFilterCmp) { + staticFilterCmp.setLayout(new GridLayout()); + final Button showSystemRoleBtn = new Button(staticFilterCmp, + SWT.CHECK); + showSystemRoleBtn.setText("Show system roles"); + showSystemRoleBtn.addSelectionListener(new SelectionAdapter() { + private static final long serialVersionUID = -7033424592697691676L; + + @Override + public void widgetSelected(SelectionEvent e) { + showSystemRoles = showSystemRoleBtn.getSelection(); + refresh(); + } + + }); + } + + @Override + protected List listFilteredElements(String filter) { + Role[] roles; + try { + StringBuilder builder = new StringBuilder(); + StringBuilder tmpBuilder = new StringBuilder(); + if (EclipseUiUtils.notEmpty(filter)) + for (String prop : knownProps) { + tmpBuilder.append("("); + tmpBuilder.append(prop); + tmpBuilder.append("=*"); + tmpBuilder.append(filter); + tmpBuilder.append("*)"); + } + if (tmpBuilder.length() > 1) { + builder.append("(&(").append(LdifName.objectClass.name()) + .append("=").append(LdifName.groupOfNames.name()) + .append(")"); + if (!showSystemRoles) + builder.append("(!(").append(LdifName.dn.name()) + .append("=*") + .append(AuthConstants.ROLES_BASEDN) + .append("))"); + builder.append("(|"); + builder.append(tmpBuilder.toString()); + builder.append("))"); + } else { + if (!showSystemRoles) + builder.append("(&(") + .append(LdifName.objectClass.name()) + .append("=") + .append(LdifName.groupOfNames.name()) + .append(")(!(").append(LdifName.dn.name()) + .append("=*") + .append(AuthConstants.ROLES_BASEDN) + .append(")))"); + else + builder.append("(").append(LdifName.objectClass.name()) + .append("=") + .append(LdifName.groupOfNames.name()) + .append(")"); + + } + roles = userAdminWrapper.getUserAdmin().getRoles( + builder.toString()); + } catch (InvalidSyntaxException e) { + throw new CmsException("Unable to get roles with filter: " + + filter, e); + } + List users = new ArrayList(); + for (Role role : roles) + if (!users.contains(role)) + users.add((User) role); + else + log.warn("Duplicated role: " + role); + + return users; + } + } + + public void refresh() { + groupTableViewerCmp.refresh(); + } + + // Override generic view methods + @Override + public void dispose() { + userAdminWrapper.removeListener(listener); + super.dispose(); + } + + @Override + public void setFocus() { + groupTableViewerCmp.setFocus(); + } + + /* DEPENDENCY INJECTION */ + public void setUserAdminWrapper(UserAdminWrapper userAdminWrapper) { + this.userAdminWrapper = userAdminWrapper; + } +} \ No newline at end of file diff --git a/org.argeo.cms.ui.workbench/src/org/argeo/security/ui/admin/internal/parts/UserBatchUpdateWizard.java b/org.argeo.cms.ui.workbench/src/org/argeo/security/ui/admin/internal/parts/UserBatchUpdateWizard.java new file mode 100644 index 000000000..31b2042b6 --- /dev/null +++ b/org.argeo.cms.ui.workbench/src/org/argeo/security/ui/admin/internal/parts/UserBatchUpdateWizard.java @@ -0,0 +1,578 @@ +package org.argeo.security.ui.admin.internal.parts; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import javax.transaction.SystemException; +import javax.transaction.UserTransaction; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.argeo.cms.CmsException; +import org.argeo.cms.auth.AuthConstants; +import org.argeo.cms.util.useradmin.UserAdminUtils; +import org.argeo.eclipse.ui.ColumnDefinition; +import org.argeo.eclipse.ui.EclipseUiUtils; +import org.argeo.eclipse.ui.parts.LdifUsersTable; +import org.argeo.jcr.ArgeoNames; +import org.argeo.osgi.useradmin.LdifName; +import org.argeo.security.ui.admin.internal.UserAdminWrapper; +import org.argeo.security.ui.admin.internal.providers.CommonNameLP; +import org.argeo.security.ui.admin.internal.providers.DomainNameLP; +import org.argeo.security.ui.admin.internal.providers.MailLP; +import org.argeo.security.ui.admin.internal.providers.UserNameLP; +import org.eclipse.jface.dialogs.IPageChangeProvider; +import org.eclipse.jface.dialogs.IPageChangedListener; +import org.eclipse.jface.dialogs.MessageDialog; +import org.eclipse.jface.dialogs.PageChangedEvent; +import org.eclipse.jface.wizard.IWizardContainer; +import org.eclipse.jface.wizard.Wizard; +import org.eclipse.jface.wizard.WizardPage; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.ModifyEvent; +import org.eclipse.swt.events.ModifyListener; +import org.eclipse.swt.events.SelectionAdapter; +import org.eclipse.swt.events.SelectionEvent; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Button; +import org.eclipse.swt.widgets.Combo; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Text; +import org.osgi.framework.InvalidSyntaxException; +import org.osgi.service.useradmin.Role; +import org.osgi.service.useradmin.User; + +/** Wizard to update users */ +public class UserBatchUpdateWizard extends Wizard { + + private final static Log log = LogFactory + .getLog(UserBatchUpdateWizard.class); + private UserAdminWrapper userAdminWrapper; + + // pages + private ChooseCommandWizardPage chooseCommandPage; + private ChooseUsersWizardPage userListPage; + private ValidateAndLaunchWizardPage validatePage; + + // Various implemented commands keys + private final static String CMD_UPDATE_PASSWORD = "resetPassword"; + private final static String CMD_GROUP_MEMBERSHIP = "groupMembership"; + + private final Map commands = new HashMap() { + private static final long serialVersionUID = 1L; + { + put("Reset password(s)", CMD_UPDATE_PASSWORD); + // TODO implement role / group management + // put("Add/Remove from group", CMD_GROUP_MEMBERSHIP); + } + }; + + public UserBatchUpdateWizard(UserAdminWrapper userAdminWrapper) { + this.userAdminWrapper = userAdminWrapper; + } + + @Override + public void addPages() { + chooseCommandPage = new ChooseCommandWizardPage(); + addPage(chooseCommandPage); + userListPage = new ChooseUsersWizardPage(); + addPage(userListPage); + validatePage = new ValidateAndLaunchWizardPage(); + addPage(validatePage); + } + + @Override + public boolean performFinish() { + if (!canFinish()) + return false; + UserTransaction ut = userAdminWrapper.getUserTransaction(); + try { + if (ut.getStatus() != javax.transaction.Status.STATUS_NO_TRANSACTION + && !MessageDialog.openConfirm(getShell(), + "Existing Transaction", + "A user transaction is already existing, " + + "are you sure you want to proceed ?")) + return false; + } catch (SystemException e) { + throw new CmsException("Cannot get user transaction state " + + "before user batch update", e); + } + + // We cannot use jobs, user modifications are still meant to be done in + // the UIThread + // UpdateJob job = null; + // if (job != null) + // job.schedule(); + + if (CMD_UPDATE_PASSWORD.equals(chooseCommandPage.getCommand())) { + char[] newValue = chooseCommandPage.getPwdValue(); + if (newValue == null) + throw new CmsException( + "Password cannot be null or an empty string"); + ResetPassword job = new ResetPassword(userAdminWrapper, + userListPage.getSelectedUsers(), newValue); + job.doUpdate(); + } + return true; + } + + public boolean canFinish() { + if (this.getContainer().getCurrentPage() == validatePage) + return true; + return false; + } + + private class ResetPassword { + private char[] newPwd; + private UserAdminWrapper userAdminWrapper; + private List usersToUpdate; + + public ResetPassword(UserAdminWrapper userAdminWrapper, + List usersToUpdate, char[] newPwd) { + this.newPwd = newPwd; + this.usersToUpdate = usersToUpdate; + this.userAdminWrapper = userAdminWrapper; + } + + @SuppressWarnings("unchecked") + protected void doUpdate() { + userAdminWrapper.beginTransactionIfNeeded(); + try { + for (User user : usersToUpdate) { + // the char array is emptied after being used. + user.getCredentials().put(null, newPwd.clone()); + } + userAdminWrapper.commitOrNotifyTransactionStateChange(); + } catch (Exception e) { + throw new CmsException("Cannot perform batch update on users", + e); + } finally { + UserTransaction ut = userAdminWrapper.getUserTransaction(); + try { + if (ut.getStatus() != javax.transaction.Status.STATUS_NO_TRANSACTION) + ut.rollback(); + } catch (IllegalStateException | SecurityException + | SystemException e) { + log.error("Unable to rollback session in 'finally', " + + "the system might be in a dirty state"); + e.printStackTrace(); + } + } + } + } + + // @SuppressWarnings("unused") + // private class AddToGroup extends UpdateJob { + // private String groupID; + // private Session session; + // + // public AddToGroup(Session session, List nodesToUpdate, + // String groupID) { + // super(session, nodesToUpdate); + // this.session = session; + // this.groupID = groupID; + // } + // + // protected void doUpdate(Node node) { + // log.info("Add/Remove to group actions are not yet implemented"); + // // TODO implement this + // // try { + // // throw new CmsException("Not yet implemented"); + // // } catch (RepositoryException re) { + // // throw new CmsException( + // // "Unable to update boolean value for node " + node, re); + // // } + // } + // } + + // /** + // * Base privileged job that will be run asynchronously to perform the + // batch + // * update + // */ + // private abstract class UpdateJob extends PrivilegedJob { + // + // private final UserAdminWrapper userAdminWrapper; + // private final List usersToUpdate; + // + // protected abstract void doUpdate(User user); + // + // public UpdateJob(UserAdminWrapper userAdminWrapper, + // List usersToUpdate) { + // super("Perform update"); + // this.usersToUpdate = usersToUpdate; + // this.userAdminWrapper = userAdminWrapper; + // } + // + // @Override + // protected IStatus doRun(IProgressMonitor progressMonitor) { + // try { + // ArgeoMonitor monitor = new EclipseArgeoMonitor(progressMonitor); + // int total = usersToUpdate.size(); + // monitor.beginTask("Performing change", total); + // userAdminWrapper.beginTransactionIfNeeded(); + // for (User user : usersToUpdate) { + // doUpdate(user); + // monitor.worked(1); + // } + // userAdminWrapper.getUserTransaction().commit(); + // } catch (Exception e) { + // throw new CmsException( + // "Cannot perform batch update on users", e); + // } finally { + // UserTransaction ut = userAdminWrapper.getUserTransaction(); + // try { + // if (ut.getStatus() != javax.transaction.Status.STATUS_NO_TRANSACTION) + // ut.rollback(); + // } catch (IllegalStateException | SecurityException + // | SystemException e) { + // log.error("Unable to rollback session in 'finally', " + // + "the system might be in a dirty state"); + // e.printStackTrace(); + // } + // } + // return Status.OK_STATUS; + // } + // } + + // PAGES + /** Displays a combo box that enables user to choose which action to perform */ + private class ChooseCommandWizardPage extends WizardPage { + private static final long serialVersionUID = -8069434295293996633L; + private Combo chooseCommandCmb; + private Button trueChk; + private Text valueTxt; + private Text pwdTxt; + private Text pwd2Txt; + + public ChooseCommandWizardPage() { + super("Choose a command to run."); + setTitle("Choose a command to run."); + } + + @Override + public void createControl(Composite parent) { + GridLayout gl = new GridLayout(); + Composite container = new Composite(parent, SWT.NO_FOCUS); + container.setLayout(gl); + + chooseCommandCmb = new Combo(container, SWT.READ_ONLY); + chooseCommandCmb.setLayoutData(EclipseUiUtils.fillWidth()); + String[] values = commands.keySet().toArray(new String[0]); + chooseCommandCmb.setItems(values); + + final Composite bottomPart = new Composite(container, SWT.NO_FOCUS); + bottomPart.setLayoutData(EclipseUiUtils.fillAll()); + bottomPart.setLayout(EclipseUiUtils.noSpaceGridLayout()); + + chooseCommandCmb.addSelectionListener(new SelectionAdapter() { + private static final long serialVersionUID = 1L; + + @Override + public void widgetSelected(SelectionEvent e) { + if (getCommand().equals(CMD_UPDATE_PASSWORD)) + populatePasswordCmp(bottomPart); + else if (getCommand().equals(CMD_GROUP_MEMBERSHIP)) + populateGroupCmp(bottomPart); + else + populateBooleanFlagCmp(bottomPart); + checkPageComplete(); + bottomPart.layout(true, true); + } + }); + setControl(container); + } + + private void populateBooleanFlagCmp(Composite parent) { + EclipseUiUtils.clear(parent); + trueChk = new Button(parent, SWT.CHECK); + trueChk.setText("Do it. (It will to the contrary if unchecked)"); + trueChk.setSelection(true); + trueChk.setLayoutData(new GridData(SWT.LEFT, SWT.TOP, false, false)); + } + + private void populatePasswordCmp(Composite parent) { + EclipseUiUtils.clear(parent); + Composite body = new Composite(parent, SWT.NO_FOCUS); + + ModifyListener ml = new ModifyListener() { + private static final long serialVersionUID = -1558726363536729634L; + + @Override + public void modifyText(ModifyEvent event) { + checkPageComplete(); + } + }; + + body.setLayout(new GridLayout(2, false)); + body.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); + pwdTxt = EclipseUiUtils.createGridLP(body, "New password", ml); + pwd2Txt = EclipseUiUtils.createGridLP(body, "Repeat password", ml); + } + + private void checkPageComplete() { + String errorMsg = null; + if (chooseCommandCmb.getSelectionIndex() < 0) + errorMsg = "Please select an action"; + else if (CMD_UPDATE_PASSWORD.equals(getCommand())) { + if (EclipseUiUtils.isEmpty(pwdTxt.getText()) + || pwdTxt.getText().length() < 4) + errorMsg = "Please enter a password that is at least 4 character long"; + else if (!pwdTxt.getText().equals(pwd2Txt.getText())) + errorMsg = "Passwords are different"; + } + if (EclipseUiUtils.notEmpty(errorMsg)) { + setMessage(errorMsg, WizardPage.ERROR); + setPageComplete(false); + } else { + setMessage("Page complete, you can proceed to user choice", + WizardPage.INFORMATION); + setPageComplete(true); + } + + getContainer().updateButtons(); + } + + private void populateGroupCmp(Composite parent) { + EclipseUiUtils.clear(parent); + trueChk = new Button(parent, SWT.CHECK); + trueChk.setText("Add to group. (It will remove user(s) from the " + + "corresponding group if unchecked)"); + trueChk.setSelection(true); + trueChk.setLayoutData(new GridData(SWT.LEFT, SWT.TOP, false, false)); + } + + protected String getCommand() { + return commands.get(chooseCommandCmb.getItem(chooseCommandCmb + .getSelectionIndex())); + } + + protected String getCommandLbl() { + return chooseCommandCmb.getItem(chooseCommandCmb + .getSelectionIndex()); + } + + @SuppressWarnings("unused") + protected boolean getBoleanValue() { + // FIXME this is not consistent and will lead to errors. + if (ArgeoNames.ARGEO_ENABLED.equals(getCommand())) + return trueChk.getSelection(); + else + return !trueChk.getSelection(); + } + + @SuppressWarnings("unused") + protected String getStringValue() { + String value = null; + if (valueTxt != null) { + value = valueTxt.getText(); + if ("".equals(value.trim())) + value = null; + } + return value; + } + + protected char[] getPwdValue() { + // We do not directly reset the password text fields: There is no + // need to over secure this process: setting a pwd to multi users + // at the same time is anyhow a bad practice and should be used only + // in test environment or for temporary access + if (pwdTxt == null || pwdTxt.isDisposed()) + return null; + else + return pwdTxt.getText().toCharArray(); + } + } + + /** + * Displays a list of users with a check box to be able to choose some of + * them + */ + private class ChooseUsersWizardPage extends WizardPage implements + IPageChangedListener { + private static final long serialVersionUID = 7651807402211214274L; + private ChooseUserTableViewer userTableCmp; + + public ChooseUsersWizardPage() { + super("Choose Users"); + setTitle("Select users who will be impacted"); + } + + @Override + public void createControl(Composite parent) { + Composite pageCmp = new Composite(parent, SWT.NONE); + pageCmp.setLayout(EclipseUiUtils.noSpaceGridLayout()); + + // Define the displayed columns + List columnDefs = new ArrayList(); + columnDefs.add(new ColumnDefinition(new CommonNameLP(), + "Common Name", 150)); + columnDefs.add(new ColumnDefinition(new MailLP(), "E-mail", 150)); + columnDefs.add(new ColumnDefinition(new DomainNameLP(), "Domain", + 200)); + + // Only show technical DN to admin + if (UserAdminUtils.isUserInRole(AuthConstants.ROLE_ADMIN)) + columnDefs.add(new ColumnDefinition(new UserNameLP(), + "Distinguished Name", 300)); + + userTableCmp = new ChooseUserTableViewer(pageCmp, SWT.MULTI + | SWT.H_SCROLL | SWT.V_SCROLL); + userTableCmp.setLayoutData(EclipseUiUtils.fillAll()); + userTableCmp.setColumnDefinitions(columnDefs); + userTableCmp.populate(true, true); + userTableCmp.refresh(); + + setControl(pageCmp); + + // Add listener to update message when shown + final IWizardContainer wContainer = this.getContainer(); + if (wContainer instanceof IPageChangeProvider) { + ((IPageChangeProvider) wContainer).addPageChangedListener(this); + } + + } + + @Override + public void pageChanged(PageChangedEvent event) { + if (event.getSelectedPage() == this) { + String msg = "Chosen batch action: " + + chooseCommandPage.getCommandLbl(); + ((WizardPage) event.getSelectedPage()).setMessage(msg); + } + } + + protected List getSelectedUsers() { + return userTableCmp.getSelectedUsers(); + } + + private class ChooseUserTableViewer extends LdifUsersTable { + private static final long serialVersionUID = 5080437561015853124L; + private final String[] knownProps = { LdifName.uid.name(), + LdifName.dn.name(), LdifName.cn.name(), + LdifName.givenName.name(), LdifName.sn.name(), + LdifName.mail.name() }; + + public ChooseUserTableViewer(Composite parent, int style) { + super(parent, style); + } + + @Override + protected List listFilteredElements(String filter) { + Role[] roles; + + try { + StringBuilder builder = new StringBuilder(); + + StringBuilder tmpBuilder = new StringBuilder(); + if (EclipseUiUtils.notEmpty(filter)) + for (String prop : knownProps) { + tmpBuilder.append("("); + tmpBuilder.append(prop); + tmpBuilder.append("=*"); + tmpBuilder.append(filter); + tmpBuilder.append("*)"); + } + if (tmpBuilder.length() > 1) { + builder.append("(&(") + .append(LdifName.objectClass.name()) + .append("=") + .append(LdifName.inetOrgPerson.name()) + .append(")(|"); + builder.append(tmpBuilder.toString()); + builder.append("))"); + } else + builder.append("(").append(LdifName.objectClass.name()) + .append("=") + .append(LdifName.inetOrgPerson.name()) + .append(")"); + roles = userAdminWrapper.getUserAdmin().getRoles( + builder.toString()); + } catch (InvalidSyntaxException e) { + throw new CmsException("Unable to get roles with filter: " + + filter, e); + } + List users = new ArrayList(); + for (Role role : roles) + // Prevent current logged in user to perform batch on + // himself + if (!UserAdminUtils.isCurrentUser((User) role)) + users.add((User) role); + return users; + } + } + } + + /** Summary of input data before launching the process */ + private class ValidateAndLaunchWizardPage extends WizardPage implements + IPageChangedListener { + private static final long serialVersionUID = 7098918351451743853L; + private ChosenUsersTableViewer userTableCmp; + + public ValidateAndLaunchWizardPage() { + super("Validate and launch"); + setTitle("Validate and launch"); + } + + @Override + public void createControl(Composite parent) { + Composite pageCmp = new Composite(parent, SWT.NO_FOCUS); + pageCmp.setLayout(EclipseUiUtils.noSpaceGridLayout()); + + List columnDefs = new ArrayList(); + columnDefs.add(new ColumnDefinition(new CommonNameLP(), + "Common Name", 150)); + columnDefs.add(new ColumnDefinition(new MailLP(), "E-mail", 150)); + columnDefs.add(new ColumnDefinition(new DomainNameLP(), "Domain", + 200)); + // Only show technical DN to admin + if (UserAdminUtils.isUserInRole(AuthConstants.ROLE_ADMIN)) + columnDefs.add(new ColumnDefinition(new UserNameLP(), + "Distinguished Name", 300)); + userTableCmp = new ChosenUsersTableViewer(pageCmp, SWT.MULTI + | SWT.H_SCROLL | SWT.V_SCROLL); + userTableCmp.setLayoutData(EclipseUiUtils.fillAll()); + userTableCmp.setColumnDefinitions(columnDefs); + userTableCmp.populate(false, false); + userTableCmp.refresh(); + setControl(pageCmp); + // Add listener to update message when shown + final IWizardContainer wContainer = this.getContainer(); + if (wContainer instanceof IPageChangeProvider) { + ((IPageChangeProvider) wContainer).addPageChangedListener(this); + } + } + + @Override + public void pageChanged(PageChangedEvent event) { + if (event.getSelectedPage() == this) { + @SuppressWarnings({ "unchecked", "rawtypes" }) + Object[] values = ((ArrayList) userListPage.getSelectedUsers()) + .toArray(new Object[userListPage.getSelectedUsers() + .size()]); + userTableCmp.getTableViewer().setInput(values); + String msg = "Following batch action: [" + + chooseCommandPage.getCommandLbl() + + "] will be perfomed on the users listed below.\n"; + // + "Are you sure you want to proceed?"; + setMessage(msg); + } + } + + private class ChosenUsersTableViewer extends LdifUsersTable { + private static final long serialVersionUID = 7814764735794270541L; + + public ChosenUsersTableViewer(Composite parent, int style) { + super(parent, style); + } + + @Override + protected List listFilteredElements(String filter) { + return userListPage.getSelectedUsers(); + } + } + } +} \ No newline at end of file diff --git a/org.argeo.cms.ui.workbench/src/org/argeo/security/ui/admin/internal/parts/UserEditor.java b/org.argeo.cms.ui.workbench/src/org/argeo/security/ui/admin/internal/parts/UserEditor.java new file mode 100644 index 000000000..6c0731d01 --- /dev/null +++ b/org.argeo.cms.ui.workbench/src/org/argeo/security/ui/admin/internal/parts/UserEditor.java @@ -0,0 +1,217 @@ +/* + * 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.ui.admin.internal.parts; + +import java.util.ArrayList; +import java.util.List; + +import org.argeo.cms.CmsException; +import org.argeo.cms.util.useradmin.UserAdminUtils; +import org.argeo.osgi.useradmin.LdifName; +import org.argeo.security.ui.admin.SecurityAdminPlugin; +import org.argeo.security.ui.admin.internal.UiUserAdminListener; +import org.argeo.security.ui.admin.internal.UserAdminWrapper; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.swt.events.ModifyEvent; +import org.eclipse.swt.events.ModifyListener; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Display; +import org.eclipse.ui.IEditorInput; +import org.eclipse.ui.IEditorSite; +import org.eclipse.ui.PartInitException; +import org.eclipse.ui.forms.AbstractFormPart; +import org.eclipse.ui.forms.editor.FormEditor; +import org.osgi.service.useradmin.Authorization; +import org.osgi.service.useradmin.Role; +import org.osgi.service.useradmin.User; +import org.osgi.service.useradmin.UserAdmin; +import org.osgi.service.useradmin.UserAdminEvent; + +/** Editor for a user, might be a user or a group. */ +public class UserEditor extends FormEditor { + private static final long serialVersionUID = 8357851520380820241L; + + public final static String USER_EDITOR_ID = SecurityAdminPlugin.PLUGIN_ID + + ".userEditor"; + public final static String GROUP_EDITOR_ID = SecurityAdminPlugin.PLUGIN_ID + + ".groupEditor"; + + /* DEPENDENCY INJECTION */ + private UserAdminWrapper userAdminWrapper; + private UserAdmin userAdmin; + + // Context + private User user; + private String username; + + private NameChangeListener listener; + + public void init(IEditorSite site, IEditorInput input) + throws PartInitException { + super.init(site, input); + username = ((UserEditorInput) getEditorInput()).getUsername(); + user = (User) userAdmin.getRole(username); + + listener = new NameChangeListener(site.getShell().getDisplay(), user); + userAdminWrapper.addListener(listener); + updateEditorTitle(null); + } + + /** + * returns the list of all authorization for the given user or of the + * current displayed user if parameter is null + */ + protected List getFlatGroups(User aUser) { + Authorization currAuth; + if (aUser == null) + currAuth = userAdmin.getAuthorization(this.user); + else + currAuth = userAdmin.getAuthorization(aUser); + + String[] roles = currAuth.getRoles(); + + List groups = new ArrayList(); + for (String roleStr : roles) { + User currRole = (User) userAdmin.getRole(roleStr); + if (!groups.contains(currRole)) + groups.add(currRole); + } + return groups; + } + + /** Exposes the user (or group) that is displayed by the current editor */ + protected User getDisplayedUser() { + return user; + } + + void updateEditorTitle(String title) { + if (title == null) { + String commonName = UserAdminUtils.getProperty(user, + LdifName.cn.name()); + title = "".equals(commonName) ? user.getName() : commonName; + } + setPartName(title); + } + + protected void addPages() { + try { + if (user.getType() == Role.GROUP) + addPage(new GroupMainPage(this, userAdminWrapper)); + else + addPage(new UserMainPage(this, userAdminWrapper)); + } catch (Exception e) { + throw new CmsException("Cannot add pages", e); + } + } + + @Override + public void doSave(IProgressMonitor monitor) { + userAdminWrapper.beginTransactionIfNeeded(); + commitPages(true); + userAdminWrapper.commitOrNotifyTransactionStateChange(); + firePropertyChange(PROP_DIRTY); + userAdminWrapper.notifyListeners(new UserAdminEvent(null, + UserAdminEvent.ROLE_REMOVED, user)); + } + + @Override + public void doSaveAs() { + } + + @Override + public boolean isSaveAsAllowed() { + return false; + } + + @Override + public void dispose() { + userAdminWrapper.removeListener(listener); + super.dispose(); + } + + // CONTROLERS FOR THIS EDITOR AND ITS PAGES + + private class NameChangeListener extends UiUserAdminListener { + + private final User user; + + public NameChangeListener(Display display, User user) { + super(display); + this.user = user; + } + + @Override + public void roleChangedToUiThread(UserAdminEvent event) { + Role changedRole = event.getRole(); + if (changedRole == null || changedRole.equals(user)) + updateEditorTitle(null); + } + } + + class MainInfoListener extends UiUserAdminListener { + private final AbstractFormPart part; + + public MainInfoListener(Display display, AbstractFormPart part) { + super(display); + this.part = part; + } + + @Override + public void roleChangedToUiThread(UserAdminEvent event) { + // Rollback + if (event.getRole() == null) + part.markStale(); + } + } + + class GroupChangeListener extends UiUserAdminListener { + private final AbstractFormPart part; + + public GroupChangeListener(Display display, AbstractFormPart part) { + super(display); + this.part = part; + } + + @Override + public void roleChangedToUiThread(UserAdminEvent event) { + // always mark as stale + part.markStale(); + } + } + + /** Registers a listener that will notify this part */ + class FormPartML implements ModifyListener { + private static final long serialVersionUID = 6299808129505381333L; + private AbstractFormPart formPart; + + public FormPartML(AbstractFormPart generalPart) { + this.formPart = generalPart; + } + + public void modifyText(ModifyEvent e) { + // Discard event when the control does not have the focus, typically + // to avoid all editors being marked as dirty during a Rollback + if (((Control) e.widget).isFocusControl()) + formPart.markDirty(); + } + } + + /* DEPENDENCY INJECTION */ + public void setUserAdminWrapper(UserAdminWrapper userAdminWrapper) { + this.userAdminWrapper = userAdminWrapper; + this.userAdmin = userAdminWrapper.getUserAdmin(); + } +} \ No newline at end of file diff --git a/org.argeo.cms.ui.workbench/src/org/argeo/security/ui/admin/internal/parts/UserEditorInput.java b/org.argeo.cms.ui.workbench/src/org/argeo/security/ui/admin/internal/parts/UserEditorInput.java new file mode 100644 index 000000000..fe129f815 --- /dev/null +++ b/org.argeo.cms.ui.workbench/src/org/argeo/security/ui/admin/internal/parts/UserEditorInput.java @@ -0,0 +1,68 @@ +/* + * 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.ui.admin.internal.parts; + +import org.eclipse.jface.resource.ImageDescriptor; +import org.eclipse.ui.IEditorInput; +import org.eclipse.ui.IPersistableElement; + +/** + * Editor input for an user defined by unique name (usually a distinguished + * name). + */ +public class UserEditorInput implements IEditorInput { + private final String username; + + public UserEditorInput(String username) { + this.username = username; + } + + public Object getAdapter(@SuppressWarnings("rawtypes") Class adapter) { + return null; + } + + public boolean exists() { + return username != null; + } + + public ImageDescriptor getImageDescriptor() { + return null; + } + + public String getName() { + return username != null ? username : ""; + } + + public IPersistableElement getPersistable() { + return null; + } + + public String getToolTipText() { + return username != null ? username : ""; + } + + public boolean equals(Object obj) { + if (!(obj instanceof UserEditorInput)) + return false; + if (((UserEditorInput) obj).getUsername() == null) + return false; + return ((UserEditorInput) obj).getUsername().equals(username); + } + + public String getUsername() { + return username; + } +} \ No newline at end of file diff --git a/org.argeo.cms.ui.workbench/src/org/argeo/security/ui/admin/internal/parts/UserMainPage.java b/org.argeo.cms.ui.workbench/src/org/argeo/security/ui/admin/internal/parts/UserMainPage.java new file mode 100644 index 000000000..db6eb538f --- /dev/null +++ b/org.argeo.cms.ui.workbench/src/org/argeo/security/ui/admin/internal/parts/UserMainPage.java @@ -0,0 +1,539 @@ +/* + * 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.ui.admin.internal.parts; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +import org.argeo.cms.CmsException; +import org.argeo.cms.auth.AuthConstants; +import org.argeo.cms.util.useradmin.UserAdminUtils; +import org.argeo.eclipse.ui.ColumnDefinition; +import org.argeo.eclipse.ui.EclipseUiUtils; +import org.argeo.eclipse.ui.parts.LdifUsersTable; +import org.argeo.jcr.ArgeoNames; +import org.argeo.osgi.useradmin.LdifName; +import org.argeo.security.ui.admin.SecurityAdminImages; +import org.argeo.security.ui.admin.internal.UserAdminWrapper; +import org.argeo.security.ui.admin.internal.parts.UserEditor.GroupChangeListener; +import org.argeo.security.ui.admin.internal.parts.UserEditor.MainInfoListener; +import org.argeo.security.ui.admin.internal.providers.CommonNameLP; +import org.argeo.security.ui.admin.internal.providers.DomainNameLP; +import org.argeo.security.ui.admin.internal.providers.RoleIconLP; +import org.argeo.security.ui.admin.internal.providers.UserFilter; +import org.argeo.security.ui.admin.internal.providers.UserNameLP; +import org.argeo.security.ui.admin.internal.providers.UserTableDefaultDClickListener; +import org.eclipse.jface.action.Action; +import org.eclipse.jface.action.ToolBarManager; +import org.eclipse.jface.resource.ImageDescriptor; +import org.eclipse.jface.viewers.ISelection; +import org.eclipse.jface.viewers.IStructuredSelection; +import org.eclipse.jface.viewers.TableViewer; +import org.eclipse.jface.viewers.Viewer; +import org.eclipse.jface.viewers.ViewerDropAdapter; +import org.eclipse.swt.SWT; +import org.eclipse.swt.dnd.DND; +import org.eclipse.swt.dnd.DropTargetEvent; +import org.eclipse.swt.dnd.TextTransfer; +import org.eclipse.swt.dnd.Transfer; +import org.eclipse.swt.dnd.TransferData; +import org.eclipse.swt.events.DisposeEvent; +import org.eclipse.swt.events.DisposeListener; +import org.eclipse.swt.events.ModifyEvent; +import org.eclipse.swt.events.ModifyListener; +import org.eclipse.swt.events.SelectionAdapter; +import org.eclipse.swt.events.SelectionEvent; +import org.eclipse.swt.graphics.Cursor; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Button; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.Text; +import org.eclipse.swt.widgets.ToolBar; +import org.eclipse.ui.forms.AbstractFormPart; +import org.eclipse.ui.forms.IManagedForm; +import org.eclipse.ui.forms.SectionPart; +import org.eclipse.ui.forms.editor.FormEditor; +import org.eclipse.ui.forms.editor.FormPage; +import org.eclipse.ui.forms.widgets.FormToolkit; +import org.eclipse.ui.forms.widgets.ScrolledForm; +import org.eclipse.ui.forms.widgets.Section; +import org.osgi.service.useradmin.Group; +import org.osgi.service.useradmin.Role; +import org.osgi.service.useradmin.User; +import org.osgi.service.useradmin.UserAdmin; +import org.osgi.service.useradmin.UserAdminEvent; + +/** Display/edit the properties of a given user */ +public class UserMainPage extends FormPage implements ArgeoNames { + final static String ID = "UserEditor.mainPage"; + + private final UserEditor editor; + private UserAdminWrapper userAdminWrapper; + + // Local configuration + private final int PRE_TITLE_INDENT = 10; + + public UserMainPage(FormEditor editor, UserAdminWrapper userAdminWrapper) { + super(editor, ID, "Main"); + this.editor = (UserEditor) editor; + this.userAdminWrapper = userAdminWrapper; + } + + protected void createFormContent(final IManagedForm mf) { + ScrolledForm form = mf.getForm(); + Composite body = form.getBody(); + GridLayout mainLayout = new GridLayout(); + // mainLayout.marginRight = 10; + body.setLayout(mainLayout); + User user = editor.getDisplayedUser(); + appendOverviewPart(body, user); + // Remove to ability to force the password for his own user. The user + // must then use the change pwd feature + if (!UserAdminUtils.isCurrentUser(user)) + appendPasswordPart(body, user); + appendMemberOfPart(body, user); + } + + /** Creates the general section */ + private void appendOverviewPart(final Composite parent, final User user) { + FormToolkit tk = getManagedForm().getToolkit(); + + Section section = addSection(tk, parent, "Main information"); + Composite body = (Composite) section.getClient(); + body.setLayout(new GridLayout(2, false)); + + final Text distinguishedName = createLT(tk, body, "User Name", + UserAdminUtils.getProperty(user, LdifName.uid.name())); + distinguishedName.setEnabled(false); + + final Text commonName = createLT(tk, body, "Common Name", + UserAdminUtils.getProperty(user, LdifName.cn.name())); + commonName.setEnabled(false); + + final Text firstName = createLT(tk, body, "First name", + UserAdminUtils.getProperty(user, LdifName.givenName.name())); + + final Text lastName = createLT(tk, body, "Last name", + UserAdminUtils.getProperty(user, LdifName.sn.name())); + + final Text email = createLT(tk, body, "Email", + UserAdminUtils.getProperty(user, LdifName.mail.name())); + + // create form part (controller) + AbstractFormPart part = new SectionPart((Section) body.getParent()) { + private MainInfoListener listener; + + @Override + public void initialize(IManagedForm form) { + super.initialize(form); + listener = editor.new MainInfoListener(parent.getDisplay(), + this); + userAdminWrapper.addListener(listener); + } + + @Override + public void dispose() { + userAdminWrapper.removeListener(listener); + super.dispose(); + } + + @SuppressWarnings("unchecked") + public void commit(boolean onSave) { + // TODO Sanity checks (mail validity...) + user.getProperties().put(LdifName.givenName.name(), + firstName.getText()); + user.getProperties() + .put(LdifName.sn.name(), lastName.getText()); + user.getProperties().put(LdifName.cn.name(), + commonName.getText()); + user.getProperties().put(LdifName.mail.name(), email.getText()); + super.commit(onSave); + } + + @Override + public void refresh() { + distinguishedName.setText(UserAdminUtils.getProperty(user, + LdifName.uid.name())); + commonName.setText(UserAdminUtils.getProperty(user, + LdifName.cn.name())); + firstName.setText(UserAdminUtils.getProperty(user, + LdifName.givenName.name())); + lastName.setText(UserAdminUtils.getProperty(user, + LdifName.sn.name())); + email.setText(UserAdminUtils.getProperty(user, + LdifName.mail.name())); + refreshFormTitle(user); + super.refresh(); + } + }; + + // Improve this: automatically generate CN when first or last name + // changes + ModifyListener cnML = new ModifyListener() { + private static final long serialVersionUID = 4298649222869835486L; + + @Override + public void modifyText(ModifyEvent event) { + String first = firstName.getText(); + String last = lastName.getText(); + String cn = first.trim() + " " + last.trim() + " "; + cn = cn.trim(); + commonName.setText(cn); + getManagedForm().getForm().setText(cn); + editor.updateEditorTitle(cn); + } + }; + firstName.addModifyListener(cnML); + lastName.addModifyListener(cnML); + + ModifyListener defaultListener = editor.new FormPartML(part); + firstName.addModifyListener(defaultListener); + lastName.addModifyListener(defaultListener); + email.addModifyListener(defaultListener); + getManagedForm().addPart(part); + } + + /** Creates the password section */ + private void appendPasswordPart(Composite parent, final User user) { + FormToolkit tk = getManagedForm().getToolkit(); + Section section = addSection(tk, parent, "Password"); + Composite body = (Composite) section.getClient(); + body.setLayout(new GridLayout(2, false)); + + // add widgets (view) + final Text password1 = createLP(tk, body, "New password", ""); + final Text password2 = createLP(tk, body, "Repeat password", ""); + + // create form part (controller) + AbstractFormPart part = new SectionPart((Section) body.getParent()) { + @SuppressWarnings("unchecked") + public void commit(boolean onSave) { + if (!password1.getText().equals("") + || !password2.getText().equals("")) { + if (password1.getText().equals(password2.getText())) { + char[] newPassword = password1.getText().toCharArray(); + // userAdminWrapper.beginTransactionIfNeeded(); + user.getCredentials().put(null, newPassword); + password1.setText(""); + password2.setText(""); + super.commit(onSave); + } else { + password1.setText(""); + password2.setText(""); + throw new CmsException("Passwords are not equals"); + } + } + } + }; + ModifyListener defaultListener = editor.new FormPartML(part); + password1.addModifyListener(defaultListener); + password2.addModifyListener(defaultListener); + getManagedForm().addPart(part); + } + + private LdifUsersTable appendMemberOfPart(final Composite parent, User user) { + FormToolkit tk = getManagedForm().getToolkit(); + Section section = addSection(tk, parent, "Roles"); + Composite body = (Composite) section.getClient(); + body.setLayout(EclipseUiUtils.noSpaceGridLayout()); + + boolean isAdmin = UserAdminUtils.isUserInRole(AuthConstants.ROLE_ADMIN); + + // Displayed columns + List columnDefs = new ArrayList(); + columnDefs.add(new ColumnDefinition(new RoleIconLP(), "", 0, 24)); + columnDefs.add(new ColumnDefinition(new CommonNameLP(), "Common Name", + 150)); + columnDefs.add(new ColumnDefinition(new DomainNameLP(), "Domain Name", + 200)); + // Only show technical DN to administrators + if (isAdmin) + columnDefs.add(new ColumnDefinition(new UserNameLP(), + "Distinguished Name", 120)); + + // Create and configure the table + final LdifUsersTable userViewerCmp = new MyUserTableViewer(body, + SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL, user); + + userViewerCmp.setColumnDefinitions(columnDefs); + if (isAdmin) + userViewerCmp.populateWithStaticFilters(false, false); + else + userViewerCmp.populate(true, false); + GridData gd = EclipseUiUtils.fillAll(); + gd.heightHint = 300; + userViewerCmp.setLayoutData(gd); + + // Controllers + TableViewer userViewer = userViewerCmp.getTableViewer(); + userViewer.addDoubleClickListener(new UserTableDefaultDClickListener()); + int operations = DND.DROP_COPY | DND.DROP_MOVE; + Transfer[] tt = new Transfer[] { TextTransfer.getInstance() }; + GroupDropListener dropL = new GroupDropListener(userAdminWrapper, + userViewer, user); + userViewer.addDropSupport(operations, tt, dropL); + + SectionPart part = new SectionPart((Section) body.getParent()) { + + private GroupChangeListener listener; + + @Override + public void initialize(IManagedForm form) { + super.initialize(form); + listener = editor.new GroupChangeListener(parent.getDisplay(), + this); + userAdminWrapper.addListener(listener); + } + + public void commit(boolean onSave) { + super.commit(onSave); + } + + @Override + public void dispose() { + userAdminWrapper.removeListener(listener); + super.dispose(); + } + + @Override + public void refresh() { + userViewerCmp.refresh(); + super.refresh(); + } + }; + getManagedForm().addPart(part); + addRemoveAbitily(part, userViewer, user); + return userViewerCmp; + } + + private class MyUserTableViewer extends LdifUsersTable { + private static final long serialVersionUID = 2653790051461237329L; + + private Button showSystemRoleBtn; + + private final User user; + private final UserFilter userFilter; + + public MyUserTableViewer(Composite parent, int style, User user) { + super(parent, style, true); + this.user = user; + userFilter = new UserFilter(); + userFilter.setShowSystemRole(false); + } + + protected void populateStaticFilters(Composite staticFilterCmp) { + staticFilterCmp.setLayout(new GridLayout()); + showSystemRoleBtn = new Button(staticFilterCmp, SWT.CHECK); + showSystemRoleBtn.setText("Show system roles"); + showSystemRoleBtn.addSelectionListener(new SelectionAdapter() { + private static final long serialVersionUID = -7033424592697691676L; + + @Override + public void widgetSelected(SelectionEvent e) { + userFilter.setShowSystemRole(showSystemRoleBtn + .getSelection()); + refresh(); + } + }); + } + + @Override + protected List listFilteredElements(String filter) { + List users = (List) editor.getFlatGroups(null); + List filteredUsers = new ArrayList(); + if (users.contains(user)) + users.remove(user); + userFilter.setSearchText(filter); + for (User user : users) + if (userFilter.select(null, null, user)) + filteredUsers.add(user); + return filteredUsers; + } + } + + private void addRemoveAbitily(SectionPart sectionPart, + TableViewer userViewer, User user) { + Section section = sectionPart.getSection(); + ToolBarManager toolBarManager = new ToolBarManager(SWT.FLAT); + ToolBar toolbar = toolBarManager.createControl(section); + final Cursor handCursor = new Cursor(section.getDisplay(), + SWT.CURSOR_HAND); + toolbar.setCursor(handCursor); + toolbar.addDisposeListener(new DisposeListener() { + private static final long serialVersionUID = 3882131405820522925L; + + public void widgetDisposed(DisposeEvent e) { + if ((handCursor != null) && (handCursor.isDisposed() == false)) { + handCursor.dispose(); + } + } + }); + + String tooltip = "Remove " + UserAdminUtils.getUsername(user) + + " from the below selected groups"; + Action action = new RemoveMembershipAction(userViewer, user, tooltip, + SecurityAdminImages.ICON_REMOVE_DESC); + toolBarManager.add(action); + toolBarManager.update(true); + section.setTextClient(toolbar); + } + + private class RemoveMembershipAction extends Action { + private static final long serialVersionUID = -1337713097184522588L; + + private final TableViewer userViewer; + private final User user; + + RemoveMembershipAction(TableViewer userViewer, User user, String name, + ImageDescriptor img) { + super(name, img); + this.userViewer = userViewer; + this.user = user; + } + + @Override + public void run() { + ISelection selection = userViewer.getSelection(); + if (selection.isEmpty()) + return; + + @SuppressWarnings("unchecked") + Iterator it = ((IStructuredSelection) selection).iterator(); + List groups = new ArrayList(); + while (it.hasNext()) { + Group currGroup = it.next(); + groups.add(currGroup); + } + + userAdminWrapper.beginTransactionIfNeeded(); + for (Group group : groups) { + group.removeMember(user); + } + userAdminWrapper.commitOrNotifyTransactionStateChange(); + for (Group group : groups) { + userAdminWrapper.notifyListeners(new UserAdminEvent(null, + UserAdminEvent.ROLE_CHANGED, group)); + } + } + } + + /** + * Defines the table as being a potential target to add group memberships + * (roles) to this user + */ + private class GroupDropListener extends ViewerDropAdapter { + private static final long serialVersionUID = 2893468717831451621L; + + private final UserAdminWrapper myUserAdminWrapper; + private final User myUser; + + public GroupDropListener(UserAdminWrapper userAdminWrapper, + Viewer userViewer, User user) { + super(userViewer); + this.myUserAdminWrapper = userAdminWrapper; + this.myUser = user; + } + + @Override + public boolean validateDrop(Object target, int operation, + TransferData transferType) { + // Target is always OK in a list only view + // TODO check if not a string + boolean validDrop = true; + return validDrop; + } + + @Override + public void drop(DropTargetEvent event) { + String name = (String) event.data; + UserAdmin myUserAdmin = myUserAdminWrapper.getUserAdmin(); + Role role = myUserAdmin.getRole(name); + // TODO this check should be done before. + if (role.getType() == Role.GROUP) { + // TODO check if the user is already member of this group + + myUserAdminWrapper.beginTransactionIfNeeded(); + Group group = (Group) role; + group.addMember(myUser); + userAdminWrapper.commitOrNotifyTransactionStateChange(); + myUserAdminWrapper.notifyListeners(new UserAdminEvent(null, + UserAdminEvent.ROLE_CHANGED, group)); + } + super.drop(event); + } + + @Override + public boolean performDrop(Object data) { + // userTableViewerCmp.refresh(); + return true; + } + } + + // LOCAL HELPERS + private void refreshFormTitle(User group) { + getManagedForm().getForm().setText( + UserAdminUtils.getProperty(group, LdifName.cn.name())); + } + + /** Appends a section with a title */ + private Section addSection(FormToolkit tk, Composite parent, String title) { + Section section = tk.createSection(parent, Section.TITLE_BAR); + GridData gd = EclipseUiUtils.fillWidth(); + gd.verticalAlignment = PRE_TITLE_INDENT; + section.setLayoutData(gd); + section.setText(title); + // section.getMenu().setVisible(true); + + Composite body = tk.createComposite(section, SWT.WRAP); + body.setLayoutData(EclipseUiUtils.fillAll()); + section.setClient(body); + + return section; + } + + /** Creates label and multiline text. */ + Text createLMT(FormToolkit toolkit, Composite body, String label, + String value) { + Label lbl = toolkit.createLabel(body, label); + lbl.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false)); + Text text = toolkit.createText(body, value, SWT.BORDER | SWT.MULTI); + text.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, true)); + return text; + } + + /** Creates label and password. */ + Text createLP(FormToolkit toolkit, Composite body, String label, + String value) { + Label lbl = toolkit.createLabel(body, label); + lbl.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false)); + Text text = toolkit.createText(body, value, SWT.BORDER | SWT.PASSWORD); + text.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false)); + return text; + } + + /** Creates label and text. */ + Text createLT(FormToolkit toolkit, Composite body, String label, + String value) { + Label lbl = toolkit.createLabel(body, label); + lbl.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false)); + Text text = toolkit.createText(body, value, SWT.BORDER); + text.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false)); + return text; + } +} \ No newline at end of file diff --git a/org.argeo.cms.ui.workbench/src/org/argeo/security/ui/admin/internal/parts/UsersView.java b/org.argeo.cms.ui.workbench/src/org/argeo/security/ui/admin/internal/parts/UsersView.java new file mode 100644 index 000000000..a397432a8 --- /dev/null +++ b/org.argeo.cms.ui.workbench/src/org/argeo/security/ui/admin/internal/parts/UsersView.java @@ -0,0 +1,194 @@ +/* + * 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.ui.admin.internal.parts; + +import java.util.ArrayList; +import java.util.List; + +import org.argeo.cms.CmsException; +import org.argeo.cms.auth.AuthConstants; +import org.argeo.cms.util.useradmin.UserAdminUtils; +import org.argeo.eclipse.ui.ColumnDefinition; +import org.argeo.eclipse.ui.EclipseUiUtils; +import org.argeo.eclipse.ui.parts.LdifUsersTable; +import org.argeo.jcr.ArgeoNames; +import org.argeo.osgi.useradmin.LdifName; +import org.argeo.security.ui.admin.SecurityAdminPlugin; +import org.argeo.security.ui.admin.internal.UiUserAdminListener; +import org.argeo.security.ui.admin.internal.UserAdminWrapper; +import org.argeo.security.ui.admin.internal.providers.CommonNameLP; +import org.argeo.security.ui.admin.internal.providers.DomainNameLP; +import org.argeo.security.ui.admin.internal.providers.MailLP; +import org.argeo.security.ui.admin.internal.providers.UserDragListener; +import org.argeo.security.ui.admin.internal.providers.UserNameLP; +import org.argeo.security.ui.admin.internal.providers.UserTableDefaultDClickListener; +import org.eclipse.jface.viewers.TableViewer; +import org.eclipse.swt.SWT; +import org.eclipse.swt.dnd.DND; +import org.eclipse.swt.dnd.TextTransfer; +import org.eclipse.swt.dnd.Transfer; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Display; +import org.eclipse.ui.part.ViewPart; +import org.osgi.framework.InvalidSyntaxException; +import org.osgi.service.useradmin.Role; +import org.osgi.service.useradmin.User; +import org.osgi.service.useradmin.UserAdminEvent; +import org.osgi.service.useradmin.UserAdminListener; + +/** List all users with filter - based on Ldif userAdmin */ +public class UsersView extends ViewPart implements ArgeoNames { + // private final static Log log = LogFactory.getLog(UsersView.class); + + public final static String ID = SecurityAdminPlugin.PLUGIN_ID + + ".usersView"; + + /* DEPENDENCY INJECTION */ + private UserAdminWrapper userAdminWrapper; + + // UI Objects + private LdifUsersTable userTableViewerCmp; + private TableViewer userViewer; + private List columnDefs = new ArrayList(); + + private UserAdminListener listener; + + @Override + public void createPartControl(Composite parent) { + + parent.setLayout(EclipseUiUtils.noSpaceGridLayout()); + // Define the displayed columns + columnDefs.add(new ColumnDefinition(new CommonNameLP(), "Common Name", + 150)); + columnDefs.add(new ColumnDefinition(new MailLP(), "E-mail", 150)); + columnDefs.add(new ColumnDefinition(new DomainNameLP(), "Domain", 200)); + // Only show technical DN to admin + if (UserAdminUtils.isUserInRole(AuthConstants.ROLE_ADMIN)) + columnDefs.add(new ColumnDefinition(new UserNameLP(), + "Distinguished Name", 300)); + + // Create and configure the table + userTableViewerCmp = new MyUserTableViewer(parent, SWT.MULTI + | SWT.H_SCROLL | SWT.V_SCROLL); + userTableViewerCmp.setLayoutData(EclipseUiUtils.fillAll()); + userTableViewerCmp.setColumnDefinitions(columnDefs); + userTableViewerCmp.populate(true, false); + + // Links + userViewer = userTableViewerCmp.getTableViewer(); + userViewer.addDoubleClickListener(new UserTableDefaultDClickListener()); + getViewSite().setSelectionProvider(userViewer); + + // Really? + userTableViewerCmp.refresh(); + + // Drag and drop + int operations = DND.DROP_COPY | DND.DROP_MOVE; + Transfer[] tt = new Transfer[] { TextTransfer.getInstance() }; + userViewer.addDragSupport(operations, tt, new UserDragListener( + userViewer)); + + // Register a useradmin listener + listener = new MyUiUAListener(parent.getDisplay()); + userAdminWrapper.addListener(listener); + } + + private class MyUiUAListener extends UiUserAdminListener { + public MyUiUAListener(Display display) { + super(display); + } + + @Override + public void roleChangedToUiThread(UserAdminEvent event) { + if (userViewer != null && !userViewer.getTable().isDisposed()) + refresh(); + } + } + + private class MyUserTableViewer extends LdifUsersTable { + private static final long serialVersionUID = 8467999509931900367L; + + private final String[] knownProps = { LdifName.uid.name(), + LdifName.dn.name(), LdifName.cn.name(), + LdifName.givenName.name(), LdifName.sn.name(), + LdifName.mail.name() }; + + public MyUserTableViewer(Composite parent, int style) { + super(parent, style); + } + + @Override + protected List listFilteredElements(String filter) { + Role[] roles; + + try { + StringBuilder builder = new StringBuilder(); + + StringBuilder tmpBuilder = new StringBuilder(); + if (EclipseUiUtils.notEmpty(filter)) + for (String prop : knownProps) { + tmpBuilder.append("("); + tmpBuilder.append(prop); + tmpBuilder.append("=*"); + tmpBuilder.append(filter); + tmpBuilder.append("*)"); + } + if (tmpBuilder.length() > 1) { + builder.append("(&(").append(LdifName.objectClass.name()) + .append("=").append(LdifName.inetOrgPerson.name()) + .append(")(|"); + builder.append(tmpBuilder.toString()); + builder.append("))"); + } else + builder.append("(").append(LdifName.objectClass.name()) + .append("=").append(LdifName.inetOrgPerson.name()) + .append(")"); + roles = userAdminWrapper.getUserAdmin().getRoles( + builder.toString()); + } catch (InvalidSyntaxException e) { + throw new CmsException("Unable to get roles with filter: " + + filter, e); + } + List users = new ArrayList(); + for (Role role : roles) + // if (role.getType() == Role.USER && role.getType() != + // Role.GROUP) + users.add((User) role); + return users; + } + } + + public void refresh() { + userTableViewerCmp.refresh(); + } + + // Override generic view methods + @Override + public void dispose() { + userAdminWrapper.removeListener(listener); + super.dispose(); + } + + @Override + public void setFocus() { + userTableViewerCmp.setFocus(); + } + + /* DEPENDENCY INJECTION */ + public void setUserAdminWrapper(UserAdminWrapper userAdminWrapper) { + this.userAdminWrapper = userAdminWrapper; + } +} \ No newline at end of file diff --git a/org.argeo.cms.ui.workbench/src/org/argeo/security/ui/admin/internal/providers/CommonNameLP.java b/org.argeo.cms.ui.workbench/src/org/argeo/security/ui/admin/internal/providers/CommonNameLP.java new file mode 100644 index 000000000..d45c0b603 --- /dev/null +++ b/org.argeo.cms.ui.workbench/src/org/argeo/security/ui/admin/internal/providers/CommonNameLP.java @@ -0,0 +1,15 @@ +package org.argeo.security.ui.admin.internal.providers; + +import org.argeo.cms.util.useradmin.UserAdminUtils; +import org.argeo.osgi.useradmin.LdifName; +import org.osgi.service.useradmin.User; + +/** Simply declare a label provider that returns the common name of a user */ +public class CommonNameLP extends UserAdminAbstractLP { + private static final long serialVersionUID = 5256703081044911941L; + + @Override + public String getText(User user) { + return UserAdminUtils.getProperty(user, LdifName.cn.name()); + } +} \ No newline at end of file diff --git a/org.argeo.cms.ui.workbench/src/org/argeo/security/ui/admin/internal/providers/DomainNameLP.java b/org.argeo.cms.ui.workbench/src/org/argeo/security/ui/admin/internal/providers/DomainNameLP.java new file mode 100644 index 000000000..795fd0af3 --- /dev/null +++ b/org.argeo.cms.ui.workbench/src/org/argeo/security/ui/admin/internal/providers/DomainNameLP.java @@ -0,0 +1,14 @@ +package org.argeo.security.ui.admin.internal.providers; + +import org.argeo.cms.util.useradmin.UserAdminUtils; +import org.osgi.service.useradmin.User; + +/** The human friendly domain name for the corresponding user. */ +public class DomainNameLP extends UserAdminAbstractLP { + private static final long serialVersionUID = 5256703081044911941L; + + @Override + public String getText(User user) { + return UserAdminUtils.getDomainName(user); + } +} \ No newline at end of file diff --git a/org.argeo.cms.ui.workbench/src/org/argeo/security/ui/admin/internal/providers/MailLP.java b/org.argeo.cms.ui.workbench/src/org/argeo/security/ui/admin/internal/providers/MailLP.java new file mode 100644 index 000000000..0a6dcb604 --- /dev/null +++ b/org.argeo.cms.ui.workbench/src/org/argeo/security/ui/admin/internal/providers/MailLP.java @@ -0,0 +1,15 @@ +package org.argeo.security.ui.admin.internal.providers; + +import org.argeo.cms.util.useradmin.UserAdminUtils; +import org.argeo.osgi.useradmin.LdifName; +import org.osgi.service.useradmin.User; + +/** Simply declare a label provider that returns the Primary Mail of a user */ +public class MailLP extends UserAdminAbstractLP { + private static final long serialVersionUID = 8329764452141982707L; + + @Override + public String getText(User user) { + return UserAdminUtils.getProperty(user, LdifName.mail.name()); + } +} \ No newline at end of file diff --git a/org.argeo.cms.ui.workbench/src/org/argeo/security/ui/admin/internal/providers/RoleIconLP.java b/org.argeo.cms.ui.workbench/src/org/argeo/security/ui/admin/internal/providers/RoleIconLP.java new file mode 100644 index 000000000..bb19220a9 --- /dev/null +++ b/org.argeo.cms.ui.workbench/src/org/argeo/security/ui/admin/internal/providers/RoleIconLP.java @@ -0,0 +1,29 @@ +package org.argeo.security.ui.admin.internal.providers; + +import org.argeo.cms.auth.AuthConstants; +import org.argeo.security.ui.admin.SecurityAdminImages; +import org.eclipse.swt.graphics.Image; +import org.osgi.service.useradmin.Role; +import org.osgi.service.useradmin.User; + +/** Provide a bundle specific image depending on the current user type */ +public class RoleIconLP extends UserAdminAbstractLP { + private static final long serialVersionUID = 6550449442061090388L; + + @Override + public String getText(User user) { + return ""; + } + + @Override + public Image getImage(Object element) { + User user = (User) element; + String dn = user.getName(); + if (dn.endsWith(AuthConstants.ROLES_BASEDN)) + return SecurityAdminImages.ICON_ROLE; + else if (user.getType() == Role.GROUP) + return SecurityAdminImages.ICON_GROUP; + else + return SecurityAdminImages.ICON_USER; + } +} \ No newline at end of file diff --git a/org.argeo.cms.ui.workbench/src/org/argeo/security/ui/admin/internal/providers/UserAdminAbstractLP.java b/org.argeo.cms.ui.workbench/src/org/argeo/security/ui/admin/internal/providers/UserAdminAbstractLP.java new file mode 100644 index 000000000..2b0a13db7 --- /dev/null +++ b/org.argeo.cms.ui.workbench/src/org/argeo/security/ui/admin/internal/providers/UserAdminAbstractLP.java @@ -0,0 +1,66 @@ +package org.argeo.security.ui.admin.internal.providers; + +import javax.naming.InvalidNameException; +import javax.naming.ldap.LdapName; + +import org.argeo.cms.CmsException; +import org.argeo.cms.util.useradmin.UserAdminUtils; +import org.eclipse.jface.resource.JFaceResources; +import org.eclipse.jface.viewers.ColumnLabelProvider; +import org.eclipse.swt.SWT; +import org.eclipse.swt.graphics.Font; +import org.eclipse.swt.widgets.Display; +import org.osgi.service.useradmin.User; + +/** + * Utility class that add font modifications to a column label provider + * depending on the given user properties + */ +public abstract class UserAdminAbstractLP extends ColumnLabelProvider { + private static final long serialVersionUID = 137336765024922368L; + + // private Font italic; + private Font bold; + + @Override + public Font getFont(Object element) { + // Self as bold + try { + LdapName selfUserName = UserAdminUtils.getCurrentUserLdapName(); + String userName = ((User) element).getName(); + LdapName userLdapName = new LdapName(userName); + if (userLdapName.equals(selfUserName)) { + if (bold == null) + bold = JFaceResources.getFontRegistry() + .defaultFontDescriptor().setStyle(SWT.BOLD) + .createFont(Display.getCurrent()); + return bold; + } + } catch (InvalidNameException e) { + throw new CmsException("cannot parse dn for " + element, e); + } + + // Disabled as Italic + // Node userProfile = (Node) elem; + // if (!userProfile.getProperty(ARGEO_ENABLED).getBoolean()) + // return italic; + + return null; + // return super.getFont(element); + } + + @Override + public String getText(Object element) { + User user = (User) element; + return getText(user); + } + + public void setDisplay(Display display) { + // italic = JFaceResources.getFontRegistry().defaultFontDescriptor() + // .setStyle(SWT.ITALIC).createFont(display); + bold = JFaceResources.getFontRegistry().defaultFontDescriptor() + .setStyle(SWT.BOLD).createFont(Display.getCurrent()); + } + + public abstract String getText(User user); +} diff --git a/org.argeo.cms.ui.workbench/src/org/argeo/security/ui/admin/internal/providers/UserDragListener.java b/org.argeo.cms.ui.workbench/src/org/argeo/security/ui/admin/internal/providers/UserDragListener.java new file mode 100644 index 000000000..f60b64c65 --- /dev/null +++ b/org.argeo.cms.ui.workbench/src/org/argeo/security/ui/admin/internal/providers/UserDragListener.java @@ -0,0 +1,40 @@ +package org.argeo.security.ui.admin.internal.providers; + +import org.eclipse.jface.viewers.IStructuredSelection; +import org.eclipse.jface.viewers.Viewer; +import org.eclipse.swt.dnd.DragSourceEvent; +import org.eclipse.swt.dnd.DragSourceListener; +import org.osgi.service.useradmin.User; + +/** Default drag listener to modify group and users via the UI */ +public class UserDragListener implements DragSourceListener { + private static final long serialVersionUID = -2074337775033781454L; + private final Viewer viewer; + + public UserDragListener(Viewer viewer) { + this.viewer = viewer; + } + + public void dragStart(DragSourceEvent event) { + // TODO implement finer checks + IStructuredSelection selection = (IStructuredSelection) viewer + .getSelection(); + if (selection.isEmpty() || selection.size() > 1) + event.doit = false; + else + event.doit = true; + } + + public void dragSetData(DragSourceEvent event) { + // TODO Support multiple selection + Object obj = ((IStructuredSelection) viewer.getSelection()) + .getFirstElement(); + if (obj != null) { + User user = (User) obj; + event.data = user.getName(); + } + } + + public void dragFinished(DragSourceEvent event) { + } +} \ No newline at end of file diff --git a/org.argeo.cms.ui.workbench/src/org/argeo/security/ui/admin/internal/providers/UserFilter.java b/org.argeo.cms.ui.workbench/src/org/argeo/security/ui/admin/internal/providers/UserFilter.java new file mode 100644 index 000000000..5f753d1c3 --- /dev/null +++ b/org.argeo.cms.ui.workbench/src/org/argeo/security/ui/admin/internal/providers/UserFilter.java @@ -0,0 +1,60 @@ +package org.argeo.security.ui.admin.internal.providers; + +import static org.argeo.eclipse.ui.EclipseUiUtils.notEmpty; + +import org.argeo.cms.auth.AuthConstants; +import org.argeo.cms.util.useradmin.UserAdminUtils; +import org.argeo.osgi.useradmin.LdifName; +import org.eclipse.jface.viewers.Viewer; +import org.eclipse.jface.viewers.ViewerFilter; +import org.osgi.service.useradmin.User; + +public class UserFilter extends ViewerFilter { + private static final long serialVersionUID = 5082509381672880568L; + + private String searchString; + private boolean showSystemRole = true; + + private final String[] knownProps = { LdifName.dn.name(), + LdifName.cn.name(), LdifName.givenName.name(), LdifName.sn.name(), + LdifName.uid.name(), LdifName.description.name(), + LdifName.mail.name() }; + + public void setSearchText(String s) { + // ensure that the value can be used for matching + if (notEmpty(s)) + searchString = ".*" + s.toLowerCase() + ".*"; + else + searchString = ".*"; + } + + public void setShowSystemRole(boolean showSystemRole) { + this.showSystemRole = showSystemRole; + } + + @Override + public boolean select(Viewer viewer, Object parentElement, Object element) { + User user = (User) element; + if (!showSystemRole + && user.getName().matches( + ".*(" + AuthConstants.ROLES_BASEDN + ")")) + // UserAdminUtils.getProperty(user, LdifName.dn.name()) + // .toLowerCase().endsWith(AuthConstants.ROLES_BASEDN)) + return false; + + if (searchString == null || searchString.length() == 0) + return true; + + if (user.getName().matches(searchString)) + return true; + + for (String key : knownProps) { + String currVal = UserAdminUtils.getProperty(user, key); + if (notEmpty(currVal) + && currVal.toLowerCase().matches(searchString)) + return true; + } + return false; + } + +} \ No newline at end of file diff --git a/org.argeo.cms.ui.workbench/src/org/argeo/security/ui/admin/internal/providers/UserNameLP.java b/org.argeo.cms.ui.workbench/src/org/argeo/security/ui/admin/internal/providers/UserNameLP.java new file mode 100644 index 000000000..a2a15c112 --- /dev/null +++ b/org.argeo.cms.ui.workbench/src/org/argeo/security/ui/admin/internal/providers/UserNameLP.java @@ -0,0 +1,13 @@ +package org.argeo.security.ui.admin.internal.providers; + +import org.osgi.service.useradmin.User; + +/** Simply declare a label provider that returns the username of a user */ +public class UserNameLP extends UserAdminAbstractLP { + private static final long serialVersionUID = 6550449442061090388L; + + @Override + public String getText(User user) { + return user.getName(); + } +} diff --git a/org.argeo.cms.ui.workbench/src/org/argeo/security/ui/admin/internal/providers/UserTableDefaultDClickListener.java b/org.argeo.cms.ui.workbench/src/org/argeo/security/ui/admin/internal/providers/UserTableDefaultDClickListener.java new file mode 100644 index 000000000..8f4a35a18 --- /dev/null +++ b/org.argeo.cms.ui.workbench/src/org/argeo/security/ui/admin/internal/providers/UserTableDefaultDClickListener.java @@ -0,0 +1,44 @@ +package org.argeo.security.ui.admin.internal.providers; + +import org.argeo.cms.CmsException; +import org.argeo.eclipse.ui.workbench.WorkbenchUiPlugin; +import org.argeo.security.ui.admin.internal.parts.UserEditor; +import org.argeo.security.ui.admin.internal.parts.UserEditorInput; +import org.eclipse.jface.viewers.DoubleClickEvent; +import org.eclipse.jface.viewers.IDoubleClickListener; +import org.eclipse.jface.viewers.IStructuredSelection; +import org.eclipse.ui.IWorkbenchPage; +import org.eclipse.ui.IWorkbenchWindow; +import org.eclipse.ui.PartInitException; +import org.osgi.service.useradmin.Group; +import org.osgi.service.useradmin.User; + +/** + * Default double click listener for the various user tables, will open the + * clicked item in the editor + */ +public class UserTableDefaultDClickListener implements IDoubleClickListener { + public void doubleClick(DoubleClickEvent evt) { + if (evt.getSelection().isEmpty()) + return; + Object obj = ((IStructuredSelection) evt.getSelection()) + .getFirstElement(); + User user = (User) obj; + IWorkbenchWindow iww = WorkbenchUiPlugin.getDefault().getWorkbench() + .getActiveWorkbenchWindow(); + IWorkbenchPage iwp = iww.getActivePage(); + UserEditorInput uei = new UserEditorInput(user.getName()); + + try { + // Works around the fact that dynamic setting of the editor icon + // causes NPE after a login/logout on RAP + if (user instanceof Group) + iwp.openEditor(uei, UserEditor.GROUP_EDITOR_ID); + else + iwp.openEditor(uei, UserEditor.USER_EDITOR_ID); + } catch (PartInitException pie) { + throw new CmsException("Unable to open UserEditor for " + user, + pie); + } + } +} \ No newline at end of file diff --git a/org.argeo.cms.ui.workbench/src/org/argeo/security/ui/admin/internal/providers/UserTransactionProvider.java b/org.argeo.cms.ui.workbench/src/org/argeo/security/ui/admin/internal/providers/UserTransactionProvider.java new file mode 100644 index 000000000..4ba304bb5 --- /dev/null +++ b/org.argeo.cms.ui.workbench/src/org/argeo/security/ui/admin/internal/providers/UserTransactionProvider.java @@ -0,0 +1,74 @@ +package org.argeo.security.ui.admin.internal.providers; + +import java.util.HashMap; +import java.util.Map; + +import javax.transaction.Status; +import javax.transaction.UserTransaction; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.argeo.cms.CmsException; +import org.argeo.security.ui.admin.SecurityAdminPlugin; +import org.eclipse.ui.AbstractSourceProvider; +import org.eclipse.ui.ISources; + +/** Observe and notify UI on UserTransaction state changes */ +public class UserTransactionProvider extends AbstractSourceProvider { + private final static Log log = LogFactory + .getLog(UserTransactionProvider.class); + + public final static String TRANSACTION_STATE = SecurityAdminPlugin.PLUGIN_ID + + ".userTransactionState"; + public final static String STATUS_ACTIVE = "status.active"; + public final static String STATUS_NO_TRANSACTION = "status.noTransaction"; + + /* DEPENDENCY INJECTION */ + private UserTransaction userTransaction; + + @Override + public String[] getProvidedSourceNames() { + return new String[] { TRANSACTION_STATE}; + } + + @Override + public Map getCurrentState() { + Map currentState = new HashMap(1); + currentState.put(TRANSACTION_STATE, getInternalCurrentState()); + return currentState; + } + + @Override + public void dispose() { + } + + private String getInternalCurrentState() { + try { + String transactionState; + if (userTransaction.getStatus() == Status.STATUS_NO_TRANSACTION) + transactionState = STATUS_NO_TRANSACTION; + else + // if (userTransaction.getStatus() == Status.STATUS_ACTIVE) + transactionState = STATUS_ACTIVE; + return transactionState; + } catch (Exception e) { + throw new CmsException("Unable to begin transaction", e); + } + } + + /** Publishes the ability to notify a state change */ + public void fireTransactionStateChange() { + try { + fireSourceChanged(ISources.WORKBENCH, TRANSACTION_STATE, + getInternalCurrentState()); + } catch (Exception e) { + log.warn("Cannot fire transaction state change event. Caught exception: " + + e.getClass().getCanonicalName() + " - " + e.getMessage()); + } + } + + /* DEPENDENCY INJECTION */ + public void setUserTransaction(UserTransaction userTransaction) { + this.userTransaction = userTransaction; + } +} \ No newline at end of file diff --git a/org.argeo.cms/pom.xml b/org.argeo.cms/pom.xml index 2a27b6334..dbbe1590d 100644 --- a/org.argeo.cms/pom.xml +++ b/org.argeo.cms/pom.xml @@ -8,22 +8,22 @@ .. org.argeo.cms - Commons CMS + Argeo CMS jar org.argeo.commons - org.argeo.cms.api + org.argeo.node.api 2.1.46-SNAPSHOT org.argeo.commons - org.argeo.server.jcr + org.argeo.jcr 2.1.46-SNAPSHOT org.argeo.commons - org.argeo.security.core + org.argeo.enterprise 2.1.46-SNAPSHOT diff --git a/org.argeo.eclipse.ui.workbench.rap/.project b/org.argeo.eclipse.ui.workbench.rap/.project deleted file mode 100644 index 751e9d794..000000000 --- a/org.argeo.eclipse.ui.workbench.rap/.project +++ /dev/null @@ -1,20 +0,0 @@ - - - org.argeo.eclipse.ui.workbench.rap - - - - - - org.eclipse.pde.ManifestBuilder - - - - org.eclipse.pde.SchemaBuilder - - - - - org.eclipse.pde.PluginNature - - diff --git a/org.argeo.eclipse.ui.workbench.rap/META-INF/spring/osgi.xml b/org.argeo.eclipse.ui.workbench.rap/META-INF/spring/osgi.xml deleted file mode 100644 index 206a72ad2..000000000 --- a/org.argeo.eclipse.ui.workbench.rap/META-INF/spring/osgi.xml +++ /dev/null @@ -1,19 +0,0 @@ - - - - - - - - - - \ No newline at end of file diff --git a/org.argeo.eclipse.ui.workbench.rap/bnd.bnd b/org.argeo.eclipse.ui.workbench.rap/bnd.bnd deleted file mode 100644 index 1b2939e64..000000000 --- a/org.argeo.eclipse.ui.workbench.rap/bnd.bnd +++ /dev/null @@ -1,4 +0,0 @@ -Bundle-SymbolicName: org.argeo.eclipse.ui.workbench.rap;singleton:=true -Bundle-ActivationPolicy: lazy - -Fragment-Host: org.eclipse.rap.ui.workbench diff --git a/org.argeo.eclipse.ui.workbench.rap/build.properties b/org.argeo.eclipse.ui.workbench.rap/build.properties deleted file mode 100644 index 485b2667a..000000000 --- a/org.argeo.eclipse.ui.workbench.rap/build.properties +++ /dev/null @@ -1,2 +0,0 @@ -source.. = src/,\ - ext/test/ diff --git a/org.argeo.eclipse.ui.workbench.rap/pom.xml b/org.argeo.eclipse.ui.workbench.rap/pom.xml deleted file mode 100644 index baf0d301f..000000000 --- a/org.argeo.eclipse.ui.workbench.rap/pom.xml +++ /dev/null @@ -1,11 +0,0 @@ - - 4.0.0 - - org.argeo.commons - 2.1.46-SNAPSHOT - argeo-commons - .. - - org.argeo.eclipse.ui.workbench.rap - Commons Eclipse Workbench RAP - diff --git a/org.argeo.eclipse.ui.workbench/pom.xml b/org.argeo.eclipse.ui.workbench/pom.xml index e12e5170d..612897f39 100644 --- a/org.argeo.eclipse.ui.workbench/pom.xml +++ b/org.argeo.eclipse.ui.workbench/pom.xml @@ -1,4 +1,5 @@ - + 4.0.0 org.argeo.commons @@ -25,5 +26,11 @@ 2.1.46-SNAPSHOT provided + + + org.argeo.commons + org.argeo.enterprise + 2.1.46-SNAPSHOT + \ No newline at end of file diff --git a/org.argeo.eclipse.ui/pom.xml b/org.argeo.eclipse.ui/pom.xml index 9c1813513..d5f26c54b 100644 --- a/org.argeo.eclipse.ui/pom.xml +++ b/org.argeo.eclipse.ui/pom.xml @@ -17,12 +17,7 @@ org.argeo.commons - org.argeo.server.jcr - 2.1.46-SNAPSHOT - - - org.argeo.commons - org.argeo.security.core + org.argeo.jcr 2.1.46-SNAPSHOT diff --git a/org.argeo.enterprise/.classpath b/org.argeo.enterprise/.classpath new file mode 100644 index 000000000..4e5da1da6 --- /dev/null +++ b/org.argeo.enterprise/.classpath @@ -0,0 +1,10 @@ + + + + + + + + diff --git a/org.argeo.enterprise/.project b/org.argeo.enterprise/.project new file mode 100644 index 000000000..5de2f0acf --- /dev/null +++ b/org.argeo.enterprise/.project @@ -0,0 +1,28 @@ + + + org.argeo.enterprise + + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.pde.ManifestBuilder + + + + + org.eclipse.pde.SchemaBuilder + + + + + + org.eclipse.jdt.core.javanature + org.eclipse.pde.PluginNature + + diff --git a/org.argeo.enterprise/bnd.bnd b/org.argeo.enterprise/bnd.bnd new file mode 100644 index 000000000..e69de29bb diff --git a/org.argeo.enterprise/build.properties b/org.argeo.enterprise/build.properties new file mode 100644 index 000000000..af03ba43b --- /dev/null +++ b/org.argeo.enterprise/build.properties @@ -0,0 +1,8 @@ +source.. = src/,\ + ext/test/ +additional.bundles = org.junit,\ + org.slf4j.commons.logging,\ + org.slf4j.api,\ + org.slf4j.log4j12,\ + org.apache.log4j,\ + bitronix.tm diff --git a/org.argeo.enterprise/ext/test/log4j.properties b/org.argeo.enterprise/ext/test/log4j.properties new file mode 100644 index 000000000..ef735668b --- /dev/null +++ b/org.argeo.enterprise/ext/test/log4j.properties @@ -0,0 +1,28 @@ +log4j.rootLogger=WARN, console + +## Levels +log4j.logger.org.argeo=TRACE + +log4j.logger.org.hibernate=WARN + +log4j.logger.org.springframework=WARN +#log4j.logger.org.springframework.web=DEBUG +#log4j.logger.org.springframework.jms=WARN +#log4j.logger.org.springframework.security=WARN + +log4j.logger.org.apache.activemq=WARN +log4j.logger.org.apache.activemq.transport=WARN +log4j.logger.org.apache.activemq.ActiveMQMessageConsumer=INFO +log4j.logger.org.apache.activemq.ActiveMQMessageProducer=INFO + +log4j.logger.org.apache.catalina=INFO +log4j.logger.org.apache.coyote=INFO +log4j.logger.org.apache.tomcat=INFO + +## Appenders +# console is set to be a ConsoleAppender. +log4j.appender.console=org.apache.log4j.ConsoleAppender + +# console uses PatternLayout. +log4j.appender.console.layout=org.apache.log4j.PatternLayout +log4j.appender.console.layout.ConversionPattern= %-5p %d{ISO8601} %m - %c%n diff --git a/org.argeo.enterprise/ext/test/org/argeo/osgi/useradmin/BasicTestConstants.java b/org.argeo.enterprise/ext/test/org/argeo/osgi/useradmin/BasicTestConstants.java new file mode 100644 index 000000000..98b8bc96d --- /dev/null +++ b/org.argeo.enterprise/ext/test/org/argeo/osgi/useradmin/BasicTestConstants.java @@ -0,0 +1,9 @@ +package org.argeo.osgi.useradmin; + +interface BasicTestConstants { + String BASE_DN = "dc=example,dc=com"; + String ROOT_USER_DN = "uid=root,ou=users," + BASE_DN; + String DEMO_USER_DN = "uid=demo,ou=users," + BASE_DN; + String ADMIN_GROUP_DN = "cn=admin,ou=groups," + BASE_DN; + String EDITORS_GROUP_DN = "cn=editors,ou=groups," + BASE_DN; +} diff --git a/org.argeo.enterprise/ext/test/org/argeo/osgi/useradmin/LdifParserTest.java b/org.argeo.enterprise/ext/test/org/argeo/osgi/useradmin/LdifParserTest.java new file mode 100644 index 000000000..fecf5dd5a --- /dev/null +++ b/org.argeo.enterprise/ext/test/org/argeo/osgi/useradmin/LdifParserTest.java @@ -0,0 +1,49 @@ +package org.argeo.osgi.useradmin; + +import java.util.ArrayList; +import java.util.Base64; +import java.util.List; +import java.util.SortedMap; + +import javax.naming.NamingEnumeration; +import javax.naming.directory.Attribute; +import javax.naming.directory.Attributes; +import javax.naming.ldap.LdapName; + +import org.argeo.util.naming.LdifParser; + +import junit.framework.TestCase; + +public class LdifParserTest extends TestCase implements BasicTestConstants { + public void testBasicLdif() throws Exception { + LdifParser ldifParser = new LdifParser(); + SortedMap res = ldifParser.read(getClass() + .getResourceAsStream("basic.ldif")); + LdapName rootDn = new LdapName(ROOT_USER_DN); + Attributes rootAttributes = res.get(rootDn); + assertNotNull(rootAttributes); + assertEquals("Superuser", + rootAttributes.get(LdifName.description.name()).get()); + byte[] rawPwEntry = (byte[]) rootAttributes.get( + LdifName.userPassword.name()).get(); + assertEquals("{SHA}ieSV55Qc+eQOaYDRSha/AjzNTJE=", + new String(rawPwEntry)); + byte[] hashedPassword = DigestUtils.sha1("demo".getBytes()); + assertEquals("{SHA}" + Base64.getEncoder().encodeToString(hashedPassword), + new String(rawPwEntry)); + + LdapName adminDn = new LdapName(ADMIN_GROUP_DN); + Attributes adminAttributes = res.get(adminDn); + assertNotNull(adminAttributes); + Attribute memberAttribute = adminAttributes.get(LdifName.member.name()); + assertNotNull(memberAttribute); + NamingEnumeration members = memberAttribute.getAll(); + List users = new ArrayList(); + while (members.hasMore()) { + Object value = members.next(); + users.add(value.toString()); + } + assertEquals(1, users.size()); + assertEquals(rootDn, new LdapName(users.get(0))); + } +} diff --git a/org.argeo.enterprise/ext/test/org/argeo/osgi/useradmin/LdifUserAdminTest.java b/org.argeo.enterprise/ext/test/org/argeo/osgi/useradmin/LdifUserAdminTest.java new file mode 100644 index 000000000..a8a7d226a --- /dev/null +++ b/org.argeo.enterprise/ext/test/org/argeo/osgi/useradmin/LdifUserAdminTest.java @@ -0,0 +1,191 @@ +package org.argeo.osgi.useradmin; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.InputStream; +import java.net.URI; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Arrays; +import java.util.Base64; +import java.util.Dictionary; +import java.util.Hashtable; +import java.util.List; +import java.util.UUID; + +import javax.transaction.TransactionManager; + +import org.osgi.service.useradmin.Authorization; +import org.osgi.service.useradmin.Group; +import org.osgi.service.useradmin.Role; +import org.osgi.service.useradmin.User; + +import bitronix.tm.BitronixTransactionManager; +import bitronix.tm.TransactionManagerServices; +import bitronix.tm.resource.ehcache.EhCacheXAResourceProducer; +import junit.framework.TestCase; + +public class LdifUserAdminTest extends TestCase implements BasicTestConstants { + private BitronixTransactionManager tm; + private URI uri; + private AbstractUserDirectory userAdmin; + + public void testConcurrent() throws Exception { + } + + @SuppressWarnings("unchecked") + public void testEdition() throws Exception { + User demoUser = (User) userAdmin.getRole(DEMO_USER_DN); + assertNotNull(demoUser); + + tm.begin(); + String newName = "demo"; + demoUser.getProperties().put("cn", newName); + assertEquals(newName, demoUser.getProperties().get("cn")); + tm.commit(); + persistAndRestart(); + assertEquals(newName, demoUser.getProperties().get("cn")); + + tm.begin(); + userAdmin.removeRole(DEMO_USER_DN); + tm.commit(); + persistAndRestart(); + + // check data + Role[] search = userAdmin.getRoles("(objectclass=inetOrgPerson)"); + assertEquals(1, search.length); + Group editorGroup = (Group) userAdmin.getRole(EDITORS_GROUP_DN); + assertNotNull(editorGroup); + Role[] members = editorGroup.getMembers(); + assertEquals(1, members.length); + } + + public void testRetrieve() throws Exception { + // users + User rootUser = (User) userAdmin.getRole(ROOT_USER_DN); + assertNotNull(rootUser); + User demoUser = (User) userAdmin.getRole(DEMO_USER_DN); + assertNotNull(demoUser); + + // groups + Group adminGroup = (Group) userAdmin.getRole(ADMIN_GROUP_DN); + assertNotNull(adminGroup); + Role[] members = adminGroup.getMembers(); + assertEquals(1, members.length); + assertEquals(rootUser, members[0]); + + Group editorGroup = (Group) userAdmin.getRole(EDITORS_GROUP_DN); + assertNotNull(editorGroup); + members = editorGroup.getMembers(); + assertEquals(2, members.length); + assertEquals(adminGroup, members[0]); + assertEquals(demoUser, members[1]); + + Authorization rootAuth = userAdmin.getAuthorization(rootUser); + List rootRoles = Arrays.asList(rootAuth.getRoles()); + assertEquals(3, rootRoles.size()); + assertTrue(rootRoles.contains(ROOT_USER_DN)); + assertTrue(rootRoles.contains(ADMIN_GROUP_DN)); + assertTrue(rootRoles.contains(EDITORS_GROUP_DN)); + + // properties + assertEquals("root@localhost", rootUser.getProperties().get("mail")); + + // credentials + byte[] hashedPassword = ("{SHA}" + Base64.getEncoder().encodeToString(DigestUtils.sha1("demo".getBytes()))) + .getBytes(); + assertTrue(rootUser.hasCredential(LdifName.userPassword.name(), hashedPassword)); + assertTrue(demoUser.hasCredential(LdifName.userPassword.name(), hashedPassword)); + + // search + Role[] search = userAdmin.getRoles(null); + assertEquals(4, search.length); + search = userAdmin.getRoles("(objectClass=groupOfNames)"); + assertEquals(2, search.length); + search = userAdmin.getRoles("(objectclass=inetOrgPerson)"); + assertEquals(2, search.length); + search = userAdmin.getRoles("(&(objectclass=inetOrgPerson)(uid=demo))"); + assertEquals(1, search.length); + } + + public void testReadWriteRead() throws Exception { + if (userAdmin instanceof LdifUserAdmin) { + Dictionary props = userAdmin.getProperties(); + ByteArrayOutputStream out = new ByteArrayOutputStream(); + ((LdifUserAdmin) userAdmin).save(out); + byte[] arr = out.toByteArray(); + out.close(); + userAdmin.destroy(); + // String written = new String(arr); + // System.out.print(written); + try (ByteArrayInputStream in = new ByteArrayInputStream(arr)) { + userAdmin = new LdifUserAdmin(props); + ((LdifUserAdmin) userAdmin).load(in); + } + Role[] search = userAdmin.getRoles(null); + assertEquals(4, search.length); + } else { + // test not relevant for LDAP + } + } + + @Override + protected void setUp() throws Exception { + Path tempDir = Files.createTempDirectory(getClass().getName()); + String uriProp = System.getProperty("argeo.userdirectory.uri"); + if (uriProp != null) + uri = new URI(uriProp); + else { + tempDir.toFile().deleteOnExit(); + Path ldifPath = tempDir.resolve(BASE_DN + ".ldif"); + try (InputStream in = getClass().getResource("basic.ldif").openStream()) { + Files.copy(in, ldifPath); + } + uri = ldifPath.toUri(); + } + + bitronix.tm.Configuration tmConf = TransactionManagerServices.getConfiguration(); + tmConf.setServerId(UUID.randomUUID().toString()); + tmConf.setLogPart1Filename(new File(tempDir.toFile(), "btm1.tlog").getAbsolutePath()); + tmConf.setLogPart2Filename(new File(tempDir.toFile(), "btm2.tlog").getAbsolutePath()); + tm = TransactionManagerServices.getTransactionManager(); + + userAdmin = initUserAdmin(uri, tm); + } + + private AbstractUserDirectory initUserAdmin(URI uri, TransactionManager tm) { + Dictionary props = new Hashtable<>(); + props.put(UserAdminConf.uri.name(), uri.toString()); + props.put(UserAdminConf.baseDn.name(), BASE_DN); + props.put(UserAdminConf.userBase.name(), "ou=users"); + props.put(UserAdminConf.groupBase.name(), "ou=groups"); + AbstractUserDirectory userAdmin; + if (uri.getScheme().startsWith("ldap")) + userAdmin = new LdapUserAdmin(props); + else + userAdmin = new LdifUserAdmin(props); + userAdmin.init(); + // JTA + EhCacheXAResourceProducer.registerXAResource(UserDirectory.class.getName(), userAdmin.getXaResource()); + userAdmin.setTransactionManager(tm); + return userAdmin; + } + + private void persistAndRestart() { + EhCacheXAResourceProducer.unregisterXAResource(UserDirectory.class.getName(), userAdmin.getXaResource()); + if (userAdmin instanceof LdifUserAdmin) + ((LdifUserAdmin) userAdmin).save(); + userAdmin.destroy(); + userAdmin = initUserAdmin(uri, tm); + } + + @Override + protected void tearDown() throws Exception { + EhCacheXAResourceProducer.unregisterXAResource(UserDirectory.class.getName(), userAdmin.getXaResource()); + tm.shutdown(); + if (userAdmin != null) + userAdmin.destroy(); + } + +} diff --git a/org.argeo.enterprise/ext/test/org/argeo/osgi/useradmin/basic.ldif b/org.argeo.enterprise/ext/test/org/argeo/osgi/useradmin/basic.ldif new file mode 100644 index 000000000..b7328b0b9 --- /dev/null +++ b/org.argeo.enterprise/ext/test/org/argeo/osgi/useradmin/basic.ldif @@ -0,0 +1,54 @@ +dn: dc=example,dc=com +objectClass: domain +objectClass: extensibleObject +objectClass: top +dc: example + +dn: ou=groups,dc=example,dc=com +objectClass: organizationalUnit +objectClass: top +ou: groups + +dn: ou=users,dc=example,dc=com +objectClass: organizationalUnit +objectClass: top +ou: users + +dn: uid=demo,ou=users,dc=example,dc=com +objectClass: inetOrgPerson +objectClass: organizationalPerson +objectClass: person +objectClass: top +cn: Demo User +description: Demo user +givenName: Demo +mail: demo@localhost +sn: User +uid: demo +userPassword:: e1NIQX1pZVNWNTVRYytlUU9hWURSU2hhL0Fqek5USkU9 + +dn: uid=root,ou=users,dc=example,dc=com +objectClass: inetOrgPerson +objectClass: person +objectClass: organizationalPerson +objectClass: top +cn: Super User +description: Superuser +givenName: Super +mail: root@localhost +sn: User +uid: root +userPassword:: e1NIQX1pZVNWNTVRYytlUU9hWURSU2hhL0Fqek5USkU9 + +dn: cn=admin,ou=groups,dc=example,dc=com +objectClass: groupOfNames +objectClass: top +cn: admin +member: uid=root,ou=users,dc=example,dc=com + +dn: cn=editors,ou=groups,dc=example,dc=com +objectClass: groupOfNames +objectClass: top +cn: editors +member: cn=admin,ou=groups,dc=example,dc=com +member: uid=demo,ou=users,dc=example,dc=com diff --git a/org.argeo.enterprise/pom.xml b/org.argeo.enterprise/pom.xml new file mode 100644 index 000000000..b8e8377cf --- /dev/null +++ b/org.argeo.enterprise/pom.xml @@ -0,0 +1,12 @@ + + + 4.0.0 + + org.argeo.commons + argeo-commons + 2.1.46-SNAPSHOT + .. + + org.argeo.enterprise + Commons Enterprise + \ No newline at end of file diff --git a/org.argeo.enterprise/src/org/argeo/osgi/useradmin/AbstractUserDirectory.java b/org.argeo.enterprise/src/org/argeo/osgi/useradmin/AbstractUserDirectory.java new file mode 100644 index 000000000..3f5bf850d --- /dev/null +++ b/org.argeo.enterprise/src/org/argeo/osgi/useradmin/AbstractUserDirectory.java @@ -0,0 +1,426 @@ +package org.argeo.osgi.useradmin; + +import static org.argeo.osgi.useradmin.LdifName.inetOrgPerson; +import static org.argeo.osgi.useradmin.LdifName.objectClass; +import static org.argeo.osgi.useradmin.LdifName.organizationalPerson; +import static org.argeo.osgi.useradmin.LdifName.person; +import static org.argeo.osgi.useradmin.LdifName.top; + +import java.io.File; +import java.net.URI; +import java.net.URISyntaxException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Dictionary; +import java.util.Enumeration; +import java.util.Hashtable; +import java.util.Iterator; +import java.util.List; + +import javax.naming.InvalidNameException; +import javax.naming.directory.Attributes; +import javax.naming.directory.BasicAttribute; +import javax.naming.directory.BasicAttributes; +import javax.naming.ldap.LdapName; +import javax.naming.ldap.Rdn; +import javax.transaction.SystemException; +import javax.transaction.Transaction; +import javax.transaction.TransactionManager; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.osgi.framework.Filter; +import org.osgi.framework.FrameworkUtil; +import org.osgi.framework.InvalidSyntaxException; +import org.osgi.service.useradmin.Authorization; +import org.osgi.service.useradmin.Role; +import org.osgi.service.useradmin.User; +import org.osgi.service.useradmin.UserAdmin; + +/** Base class for a {@link UserDirectory}. */ +public abstract class AbstractUserDirectory implements UserAdmin, UserDirectory { + private final static Log log = LogFactory.getLog(AbstractUserDirectory.class); + + private final Hashtable properties; + private final LdapName baseDn; + private final String userObjectClass, userBase, groupObjectClass, groupBase; + + private final boolean readOnly; + private final URI uri; + + private UserAdmin externalRoles; + private List indexedUserProperties = Arrays + .asList(new String[] { LdifName.uid.name(), LdifName.mail.name(), LdifName.cn.name() }); + + private String memberAttributeId = "member"; + private List credentialAttributeIds = Arrays.asList(new String[] { LdifName.userPassword.name() }); + + // JTA + private TransactionManager transactionManager; + private WcXaResource xaResource = new WcXaResource(this); + + public AbstractUserDirectory(Dictionary props) { + properties = new Hashtable(); + for (Enumeration keys = props.keys(); keys.hasMoreElements();) { + String key = keys.nextElement(); + properties.put(key, props.get(key)); + } + + String uriStr = UserAdminConf.uri.getValue(properties); + if (uriStr == null) + uri = null; + else + try { + uri = new URI(uriStr); + } catch (URISyntaxException e) { + throw new UserDirectoryException("Badly formatted URI " + uriStr, e); + } + + try { + baseDn = new LdapName(UserAdminConf.baseDn.getValue(properties)); + } catch (InvalidNameException e) { + throw new UserDirectoryException("Badly formated base DN " + UserAdminConf.baseDn.getValue(properties), e); + } + String readOnlyStr = UserAdminConf.readOnly.getValue(properties); + if (readOnlyStr == null) { + readOnly = readOnlyDefault(uri); + properties.put(UserAdminConf.readOnly.name(), Boolean.toString(readOnly)); + } else + readOnly = new Boolean(readOnlyStr); + + userObjectClass = UserAdminConf.userObjectClass.getValue(properties); + userBase = UserAdminConf.userBase.getValue(properties); + groupObjectClass = UserAdminConf.groupObjectClass.getValue(properties); + groupBase = UserAdminConf.groupBase.getValue(properties); + } + + /** Returns the groups this user is a direct member of. */ + protected abstract List getDirectGroups(LdapName dn); + + protected abstract Boolean daoHasRole(LdapName dn); + + protected abstract DirectoryUser daoGetRole(LdapName key); + + protected abstract List doGetRoles(Filter f); + + public void init() { + + } + + public void destroy() { + + } + + protected boolean isEditing() { + return xaResource.wc() != null; + } + + protected UserDirectoryWorkingCopy getWorkingCopy() { + UserDirectoryWorkingCopy wc = xaResource.wc(); + if (wc == null) + return null; + return wc; + } + + protected void checkEdit() { + Transaction transaction; + try { + transaction = transactionManager.getTransaction(); + } catch (SystemException e) { + throw new UserDirectoryException("Cannot get transaction", e); + } + if (transaction == null) + throw new UserDirectoryException("A transaction needs to be active in order to edit"); + if (xaResource.wc() == null) { + try { + transaction.enlistResource(xaResource); + } catch (Exception e) { + throw new UserDirectoryException("Cannot enlist " + xaResource, e); + } + } else { + } + } + + protected List getAllRoles(DirectoryUser user) { + List allRoles = new ArrayList(); + if (user != null) { + collectRoles(user, allRoles); + allRoles.add(user); + } else + collectAnonymousRoles(allRoles); + return allRoles; + } + + private void collectRoles(DirectoryUser user, List allRoles) { + for (LdapName groupDn : getDirectGroups(user.getDn())) { + // TODO check for loops + DirectoryUser group = doGetRole(groupDn); + allRoles.add(group); + collectRoles(group, allRoles); + } + } + + private void collectAnonymousRoles(List allRoles) { + // TODO gather anonymous roles + } + + // USER ADMIN + @Override + public Role getRole(String name) { + return doGetRole(toDn(name)); + } + + protected DirectoryUser doGetRole(LdapName dn) { + UserDirectoryWorkingCopy wc = getWorkingCopy(); + DirectoryUser user = daoGetRole(dn); + if (wc != null) { + if (user == null && wc.getNewUsers().containsKey(dn)) + user = wc.getNewUsers().get(dn); + else if (wc.getDeletedUsers().containsKey(dn)) + user = null; + } + return user; + } + + @SuppressWarnings("unchecked") + @Override + public Role[] getRoles(String filter) throws InvalidSyntaxException { + UserDirectoryWorkingCopy wc = getWorkingCopy(); + Filter f = filter != null ? FrameworkUtil.createFilter(filter) : null; + List res = doGetRoles(f); + if (wc != null) { + for (Iterator it = res.iterator(); it.hasNext();) { + DirectoryUser user = it.next(); + LdapName dn = user.getDn(); + if (wc.getDeletedUsers().containsKey(dn)) + it.remove(); + } + for (DirectoryUser user : wc.getNewUsers().values()) { + if (f == null || f.match(user.getProperties())) + res.add(user); + } + // no need to check modified users, + // since doGetRoles was already based on the modified attributes + } + return res.toArray(new Role[res.size()]); + } + + @Override + public User getUser(String key, String value) { + // TODO check value null or empty + List collectedUsers = new ArrayList(getIndexedUserProperties().size()); + if (key != null) { + doGetUser(key, value, collectedUsers); + } else { + // try dn + DirectoryUser user = null; + try { + user = (DirectoryUser) getRole(value); + if (user != null) + collectedUsers.add(user); + } catch (Exception e) { + // silent + } + // try all indexes + for (String attr : getIndexedUserProperties()) + doGetUser(attr, value, collectedUsers); + } + if (collectedUsers.size() == 1) + return collectedUsers.get(0); + else if (collectedUsers.size() > 1) + log.warn(collectedUsers.size() + " users for " + (key != null ? key + "=" : "") + value); + return null; + } + + protected void doGetUser(String key, String value, List collectedUsers) { + try { + Filter f = FrameworkUtil.createFilter("(" + key + "=" + value + ")"); + List users = doGetRoles(f); + collectedUsers.addAll(users); + } catch (InvalidSyntaxException e) { + throw new UserDirectoryException("Cannot get user with " + key + "=" + value, e); + } + } + + @Override + public Authorization getAuthorization(User user) { + return new LdifAuthorization((DirectoryUser) user, getAllRoles((DirectoryUser) user)); + } + + @Override + public Role createRole(String name, int type) { + checkEdit(); + UserDirectoryWorkingCopy wc = getWorkingCopy(); + LdapName dn = toDn(name); + if ((daoHasRole(dn) && !wc.getDeletedUsers().containsKey(dn)) || wc.getNewUsers().containsKey(dn)) + throw new UserDirectoryException("Already a role " + name); + BasicAttributes attrs = new BasicAttributes(true); + // attrs.put(LdifName.dn.name(), dn.toString()); + Rdn nameRdn = dn.getRdn(dn.size() - 1); + // TODO deal with multiple attr RDN + attrs.put(nameRdn.getType(), nameRdn.getValue()); + if (wc.getDeletedUsers().containsKey(dn)) { + wc.getDeletedUsers().remove(dn); + wc.getModifiedUsers().put(dn, attrs); + } else { + wc.getModifiedUsers().put(dn, attrs); + DirectoryUser newRole = newRole(dn, type, attrs); + wc.getNewUsers().put(dn, newRole); + } + return getRole(name); + } + + protected DirectoryUser newRole(LdapName dn, int type, Attributes attrs) { + LdifUser newRole; + BasicAttribute objClass = new BasicAttribute(objectClass.name()); + if (type == Role.USER) { + String userObjClass = newUserObjectClass(dn); + objClass.add(userObjClass); + if (inetOrgPerson.name().equals(userObjClass)) { + objClass.add(organizationalPerson.name()); + objClass.add(person.name()); + } else if (organizationalPerson.name().equals(userObjClass)) { + objClass.add(person.name()); + } + objClass.add(top.name()); + attrs.put(objClass); + newRole = new LdifUser(this, dn, attrs); + } else if (type == Role.GROUP) { + String groupObjClass = getGroupObjectClass(); + objClass.add(groupObjClass); + // objClass.add(LdifName.extensibleObject.name()); + objClass.add(top.name()); + attrs.put(objClass); + newRole = new LdifGroup(this, dn, attrs); + } else + throw new UserDirectoryException("Unsupported type " + type); + return newRole; + } + + @Override + public boolean removeRole(String name) { + checkEdit(); + UserDirectoryWorkingCopy wc = getWorkingCopy(); + LdapName dn = toDn(name); + boolean actuallyDeleted; + if (daoHasRole(dn) || wc.getNewUsers().containsKey(dn)) { + DirectoryUser user = (DirectoryUser) getRole(name); + wc.getDeletedUsers().put(dn, user); + actuallyDeleted = true; + } else {// just removing from groups (e.g. system roles) + actuallyDeleted = false; + } + for (LdapName groupDn : getDirectGroups(dn)) { + DirectoryUser group = doGetRole(groupDn); + group.getAttributes().get(getMemberAttributeId()).remove(dn.toString()); + } + return actuallyDeleted; + } + + // TRANSACTION + protected void prepare(UserDirectoryWorkingCopy wc) { + + } + + protected void commit(UserDirectoryWorkingCopy wc) { + + } + + protected void rollback(UserDirectoryWorkingCopy wc) { + + } + + // UTILITIES + protected LdapName toDn(String name) { + try { + return new LdapName(name); + } catch (InvalidNameException e) { + throw new UserDirectoryException("Badly formatted name", e); + } + } + + // GETTERS + protected String getMemberAttributeId() { + return memberAttributeId; + } + + protected List getCredentialAttributeIds() { + return credentialAttributeIds; + } + + protected URI getUri() { + return uri; + } + + protected List getIndexedUserProperties() { + return indexedUserProperties; + } + + protected void setIndexedUserProperties(List indexedUserProperties) { + this.indexedUserProperties = indexedUserProperties; + } + + private static boolean readOnlyDefault(URI uri) { + if (uri == null) + return true; + if (uri.getScheme().equals("file")) { + File file = new File(uri); + if (file.exists()) + return !file.canWrite(); + else + return !file.getParentFile().canWrite(); + } + return true; + } + + public boolean isReadOnly() { + return readOnly; + } + + protected UserAdmin getExternalRoles() { + return externalRoles; + } + + public LdapName getBaseDn() { + // always clone so that the property is not modified by reference + return (LdapName) baseDn.clone(); + } + + /** dn can be null, in that case a default should be returned. */ + public String getUserObjectClass() { + return userObjectClass; + } + + public String getUserBase() { + return userBase; + } + + protected String newUserObjectClass(LdapName dn) { + return getUserObjectClass(); + } + + public String getGroupObjectClass() { + return groupObjectClass; + } + + public String getGroupBase() { + return groupBase; + } + + public Dictionary getProperties() { + return properties; + } + + public void setExternalRoles(UserAdmin externalRoles) { + this.externalRoles = externalRoles; + } + + public void setTransactionManager(TransactionManager transactionManager) { + this.transactionManager = transactionManager; + } + + public WcXaResource getXaResource() { + return xaResource; + } + +} diff --git a/org.argeo.enterprise/src/org/argeo/osgi/useradmin/AggregatingAuthorization.java b/org.argeo.enterprise/src/org/argeo/osgi/useradmin/AggregatingAuthorization.java new file mode 100644 index 000000000..f270b8d89 --- /dev/null +++ b/org.argeo.enterprise/src/org/argeo/osgi/useradmin/AggregatingAuthorization.java @@ -0,0 +1,73 @@ +package org.argeo.osgi.useradmin; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.List; + +import javax.security.auth.x500.X500Principal; + +import org.osgi.service.useradmin.Authorization; + +class AggregatingAuthorization implements Authorization { + private final String name; + private final String displayName; + private final List systemRoles; + private final List roles; + + public AggregatingAuthorization(String name, String displayName, + Collection systemRoles, String[] roles) { + this.name = new X500Principal(name).getName(); + this.displayName = displayName; + this.systemRoles = Collections.unmodifiableList(new ArrayList( + systemRoles)); + this.roles = Collections.unmodifiableList(Arrays.asList(roles)); + } + + @Override + public String getName() { + return name; + } + + @Override + public boolean hasRole(String name) { + if (systemRoles.contains(name)) + return true; + if (roles.contains(name)) + return true; + return false; + } + + @Override + public String[] getRoles() { + int size = systemRoles.size() + roles.size(); + List res = new ArrayList(size); + res.addAll(systemRoles); + res.addAll(roles); + return res.toArray(new String[size]); + } + + @Override + public int hashCode() { + if (name == null) + return super.hashCode(); + return name.hashCode(); + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof Authorization)) + return false; + Authorization that = (Authorization) obj; + if (name == null) + return that.getName() == null; + return name.equals(that.getName()); + } + + @Override + public String toString() { + return displayName; + } + +} diff --git a/org.argeo.enterprise/src/org/argeo/osgi/useradmin/AggregatingUserAdmin.java b/org.argeo.enterprise/src/org/argeo/osgi/useradmin/AggregatingUserAdmin.java new file mode 100644 index 000000000..860c5efc9 --- /dev/null +++ b/org.argeo.enterprise/src/org/argeo/osgi/useradmin/AggregatingUserAdmin.java @@ -0,0 +1,189 @@ +package org.argeo.osgi.useradmin; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Dictionary; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Hashtable; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import javax.naming.InvalidNameException; +import javax.naming.ldap.LdapName; + +import org.osgi.framework.InvalidSyntaxException; +import org.osgi.service.useradmin.Authorization; +import org.osgi.service.useradmin.Role; +import org.osgi.service.useradmin.User; +import org.osgi.service.useradmin.UserAdmin; + +/** + * Aggregates multiple {@link UserDirectory} and integrates them with system + * roles. + */ +public class AggregatingUserAdmin implements UserAdmin { + private final LdapName systemRolesBaseDn; + + // DAOs + private AbstractUserDirectory systemRoles = null; + private Map businessRoles = new HashMap(); + + public AggregatingUserAdmin(String systemRolesBaseDn) { + try { + this.systemRolesBaseDn = new LdapName(systemRolesBaseDn); + } catch (InvalidNameException e) { + throw new UserDirectoryException("Cannot initialize " + AggregatingUserAdmin.class, e); + } + } + + @Override + public Role createRole(String name, int type) { + return findUserAdmin(name).createRole(name, type); + } + + @Override + public boolean removeRole(String name) { + boolean actuallyDeleted = findUserAdmin(name).removeRole(name); + systemRoles.removeRole(name); + return actuallyDeleted; + } + + @Override + public Role getRole(String name) { + return findUserAdmin(name).getRole(name); + } + + @Override + public Role[] getRoles(String filter) throws InvalidSyntaxException { + List res = new ArrayList(); + for (UserAdmin userAdmin : businessRoles.values()) { + res.addAll(Arrays.asList(userAdmin.getRoles(filter))); + } + res.addAll(Arrays.asList(systemRoles.getRoles(filter))); + return res.toArray(new Role[res.size()]); + } + + @Override + public User getUser(String key, String value) { + List res = new ArrayList(); + for (UserAdmin userAdmin : businessRoles.values()) { + User u = userAdmin.getUser(key, value); + if (u != null) + res.add(u); + } + // Note: node roles cannot contain users, so it is not searched + return res.size() == 1 ? res.get(0) : null; + } + + @Override + public Authorization getAuthorization(User user) { + if (user == null) {// anonymous + return systemRoles.getAuthorization(null); + } + UserAdmin userAdmin = findUserAdmin(user.getName()); + Authorization rawAuthorization = userAdmin.getAuthorization(user); + // gather system roles + Set sysRoles = new HashSet(); + for (String role : rawAuthorization.getRoles()) { + Authorization auth = systemRoles.getAuthorization((User) userAdmin.getRole(role)); + sysRoles.addAll(Arrays.asList(auth.getRoles())); + } + Authorization authorization = new AggregatingAuthorization(rawAuthorization.getName(), + rawAuthorization.toString(), sysRoles, rawAuthorization.getRoles()); + return authorization; + } + + // + // USER ADMIN AGGREGATOR + // + protected void addUserDirectory(AbstractUserDirectory userDirectory) { + LdapName baseDn = userDirectory.getBaseDn(); + if (isSystemRolesBaseDn(baseDn)) { + this.systemRoles = userDirectory; + systemRoles.setExternalRoles(this); + } else { + if (businessRoles.containsKey(baseDn)) + throw new UserDirectoryException("There is already a user admin for " + baseDn); + businessRoles.put(baseDn, userDirectory); + } + userDirectory.init(); + postAdd(userDirectory); + } + + /** Called after a new user directory has been added */ + protected void postAdd(AbstractUserDirectory userDirectory) { + } + + private UserAdmin findUserAdmin(String name) { + try { + return findUserAdmin(new LdapName(name)); + } catch (InvalidNameException e) { + throw new UserDirectoryException("Badly formatted name " + name, e); + } + } + + private UserAdmin findUserAdmin(LdapName name) { + if (name.startsWith(systemRolesBaseDn)) + return systemRoles; + List res = new ArrayList(1); + for (LdapName baseDn : businessRoles.keySet()) { + if (name.startsWith(baseDn)) + res.add(businessRoles.get(baseDn)); + } + if (res.size() == 0) + throw new UserDirectoryException("Cannot find user admin for " + name); + if (res.size() > 1) + throw new UserDirectoryException("Multiple user admin found for " + name); + return res.get(0); + } + + protected boolean isSystemRolesBaseDn(LdapName baseDn) { + return baseDn.equals(systemRolesBaseDn); + } + + protected Dictionary currentState() { + Dictionary res = new Hashtable(); + // res.put(NodeConstants.CN, NodeConstants.DEFAULT); + for (LdapName name : businessRoles.keySet()) { + AbstractUserDirectory userDirectory = businessRoles.get(name); + String uri = UserAdminConf.propertiesAsUri(userDirectory.getProperties()).toString(); + res.put(uri, ""); + } + return res; + } + + public void destroy() { + for (LdapName name : businessRoles.keySet()) { + AbstractUserDirectory userDirectory = businessRoles.get(name); + destroy(userDirectory); + } + businessRoles.clear(); + businessRoles = null; + destroy(systemRoles); + systemRoles = null; + } + + private void destroy(AbstractUserDirectory userDirectory) { + preDestroy(userDirectory); + userDirectory.destroy(); + } + + protected void removeUserDirectory(LdapName baseDn) { + if (isSystemRolesBaseDn(baseDn)) + throw new UserDirectoryException("System roles cannot be removed "); + if (!businessRoles.containsKey(baseDn)) + throw new UserDirectoryException("No user directory registered for " + baseDn); + AbstractUserDirectory userDirectory = businessRoles.remove(baseDn); + destroy(userDirectory); + } + + /** + * Called before each user directory is destroyed, so that additional + * actions can be performed. + */ + protected void preDestroy(UserDirectory userDirectory) { + } + +} diff --git a/org.argeo.enterprise/src/org/argeo/osgi/useradmin/DigestUtils.java b/org.argeo.enterprise/src/org/argeo/osgi/useradmin/DigestUtils.java new file mode 100644 index 000000000..d8f8ce9d5 --- /dev/null +++ b/org.argeo.enterprise/src/org/argeo/osgi/useradmin/DigestUtils.java @@ -0,0 +1,20 @@ +package org.argeo.osgi.useradmin; + +import java.security.MessageDigest; + +class DigestUtils { + static byte[] sha1(byte[] bytes) { + try { + MessageDigest digest = MessageDigest.getInstance("SHA1"); + digest.update(bytes); + byte[] checksum = digest.digest(); + return checksum; + } catch (Exception e) { + throw new UserDirectoryException("Cannot SHA1 digest", e); + } + } + + private DigestUtils() { + } + +} diff --git a/org.argeo.enterprise/src/org/argeo/osgi/useradmin/DirectoryGroup.java b/org.argeo.enterprise/src/org/argeo/osgi/useradmin/DirectoryGroup.java new file mode 100644 index 000000000..7f8046313 --- /dev/null +++ b/org.argeo.enterprise/src/org/argeo/osgi/useradmin/DirectoryGroup.java @@ -0,0 +1,12 @@ +package org.argeo.osgi.useradmin; + +import java.util.List; + +import javax.naming.ldap.LdapName; + +import org.osgi.service.useradmin.Group; + +/** A group in a user directroy. */ +interface DirectoryGroup extends Group, DirectoryUser { + List getMemberNames(); +} diff --git a/org.argeo.enterprise/src/org/argeo/osgi/useradmin/DirectoryUser.java b/org.argeo.enterprise/src/org/argeo/osgi/useradmin/DirectoryUser.java new file mode 100644 index 000000000..146b80578 --- /dev/null +++ b/org.argeo.enterprise/src/org/argeo/osgi/useradmin/DirectoryUser.java @@ -0,0 +1,15 @@ +package org.argeo.osgi.useradmin; + +import javax.naming.directory.Attributes; +import javax.naming.ldap.LdapName; + +import org.osgi.service.useradmin.User; + +/** A user in a user directory. */ +interface DirectoryUser extends User { + LdapName getDn(); + + Attributes getAttributes(); + + void publishAttributes(Attributes modifiedAttributes); +} diff --git a/org.argeo.enterprise/src/org/argeo/osgi/useradmin/LdapUserAdmin.java b/org.argeo.enterprise/src/org/argeo/osgi/useradmin/LdapUserAdmin.java new file mode 100644 index 000000000..7a617dfd6 --- /dev/null +++ b/org.argeo.enterprise/src/org/argeo/osgi/useradmin/LdapUserAdmin.java @@ -0,0 +1,227 @@ +package org.argeo.osgi.useradmin; + +import static org.argeo.osgi.useradmin.LdifName.objectClass; + +import java.util.ArrayList; +import java.util.Dictionary; +import java.util.Hashtable; +import java.util.List; + +import javax.naming.Binding; +import javax.naming.Context; +import javax.naming.InvalidNameException; +import javax.naming.NameNotFoundException; +import javax.naming.NamingEnumeration; +import javax.naming.NamingException; +import javax.naming.directory.Attribute; +import javax.naming.directory.Attributes; +import javax.naming.directory.DirContext; +import javax.naming.directory.SearchControls; +import javax.naming.directory.SearchResult; +import javax.naming.ldap.InitialLdapContext; +import javax.naming.ldap.LdapName; +import javax.transaction.TransactionManager; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.osgi.framework.Filter; + +/** + * A user admin based on a LDAP server. Requires a {@link TransactionManager} + * and an open transaction for write access. + */ +public class LdapUserAdmin extends AbstractUserDirectory { + private final static Log log = LogFactory.getLog(LdapUserAdmin.class); + + private InitialLdapContext initialLdapContext = null; + + public LdapUserAdmin(Dictionary properties) { + super(properties); + try { + Hashtable connEnv = new Hashtable(); + connEnv.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory"); + connEnv.put(Context.PROVIDER_URL, getUri().toString()); + connEnv.put("java.naming.ldap.attributes.binary", LdifName.userPassword.name()); + + initialLdapContext = new InitialLdapContext(connEnv, null); + // StartTlsResponse tls = (StartTlsResponse) ctx + // .extendedOperation(new StartTlsRequest()); + // tls.negotiate(); + initialLdapContext.addToEnvironment(Context.SECURITY_AUTHENTICATION, "simple"); + Object principal = properties.get(Context.SECURITY_PRINCIPAL); + if (principal != null) { + initialLdapContext.addToEnvironment(Context.SECURITY_PRINCIPAL, principal.toString()); + Object creds = properties.get(Context.SECURITY_CREDENTIALS); + if (creds != null) { + initialLdapContext.addToEnvironment(Context.SECURITY_CREDENTIALS, creds.toString()); + + } + } + // initialLdapContext.addToEnvironment(Context.SECURITY_PRINCIPAL, + // "uid=admin,ou=system"); + // initialLdapContext.addToEnvironment(Context.SECURITY_CREDENTIALS, + // "secret"); + } catch (Exception e) { + throw new UserDirectoryException("Cannot connect to LDAP", e); + } + } + + public void destroy() { + try { + // tls.close(); + initialLdapContext.close(); + } catch (NamingException e) { + log.error("Cannot destroy LDAP user admin", e); + } + } + + protected InitialLdapContext getLdapContext() { + return initialLdapContext; + } + + @Override + protected Boolean daoHasRole(LdapName dn) { + return daoGetRole(dn) != null; + } + + @Override + protected DirectoryUser daoGetRole(LdapName name) { + try { + Attributes attrs = getLdapContext().getAttributes(name); + if (attrs.size() == 0) + return null; + LdifUser res; + if (attrs.get(objectClass.name()).contains(getGroupObjectClass())) + res = new LdifGroup(this, name, attrs); + else if (attrs.get(objectClass.name()).contains(getUserObjectClass())) + res = new LdifUser(this, name, attrs); + else + throw new UserDirectoryException("Unsupported LDAP type for " + name); + return res; + } catch (NamingException e) { + return null; + } + } + + @Override + protected List doGetRoles(Filter f) { + try { + String searchFilter = f != null ? f.toString() + : "(|(" + objectClass + "=" + getUserObjectClass() + ")(" + objectClass + "=" + + getGroupObjectClass() + "))"; + SearchControls searchControls = new SearchControls(); + searchControls.setSearchScope(SearchControls.SUBTREE_SCOPE); + + LdapName searchBase = getBaseDn(); + NamingEnumeration results = getLdapContext().search(searchBase, searchFilter, searchControls); + + ArrayList res = new ArrayList(); + results: while (results.hasMoreElements()) { + SearchResult searchResult = results.next(); + Attributes attrs = searchResult.getAttributes(); + Attribute objectClassAttr = attrs.get(objectClass.name()); + LdapName dn = toDn(searchBase, searchResult); + LdifUser role; + if (objectClassAttr.contains(getGroupObjectClass())) + role = new LdifGroup(this, dn, attrs); + else if (objectClassAttr.contains(getUserObjectClass())) + role = new LdifUser(this, dn, attrs); + else { + log.warn("Unsupported LDAP type for " + searchResult.getName()); + continue results; + } + res.add(role); + } + return res; + } catch (Exception e) { + throw new UserDirectoryException("Cannot get roles for filter " + f, e); + } + } + + private LdapName toDn(LdapName baseDn, Binding binding) throws InvalidNameException { + return new LdapName(binding.isRelative() ? binding.getName() + "," + baseDn : binding.getName()); + } + + @Override + protected List getDirectGroups(LdapName dn) { + List directGroups = new ArrayList(); + try { + String searchFilter = "(&(" + objectClass + "=" + getGroupObjectClass() + ")(" + getMemberAttributeId() + + "=" + dn + "))"; + + SearchControls searchControls = new SearchControls(); + searchControls.setSearchScope(SearchControls.SUBTREE_SCOPE); + + LdapName searchBase = getBaseDn(); + NamingEnumeration results = getLdapContext().search(searchBase, searchFilter, searchControls); + + while (results.hasMoreElements()) { + SearchResult searchResult = (SearchResult) results.nextElement(); + directGroups.add(toDn(searchBase, searchResult)); + } + return directGroups; + } catch (Exception e) { + throw new UserDirectoryException("Cannot populate direct members of " + dn, e); + } + } + + @Override + protected void prepare(UserDirectoryWorkingCopy wc) { + try { + getLdapContext().reconnect(getLdapContext().getConnectControls()); + // delete + for (LdapName dn : wc.getDeletedUsers().keySet()) { + if (!entryExists(dn)) + throw new UserDirectoryException("User to delete no found " + dn); + } + // add + for (LdapName dn : wc.getNewUsers().keySet()) { + if (entryExists(dn)) + throw new UserDirectoryException("User to create found " + dn); + } + // modify + for (LdapName dn : wc.getModifiedUsers().keySet()) { + if (!wc.getNewUsers().containsKey(dn) && !entryExists(dn)) + throw new UserDirectoryException("User to modify not found " + dn); + } + } catch (NamingException e) { + throw new UserDirectoryException("Cannot prepare LDAP", e); + } + } + + private boolean entryExists(LdapName dn) throws NamingException { + try { + return getLdapContext().getAttributes(dn).size() != 0; + } catch (NameNotFoundException e) { + return false; + } + } + + @Override + protected void commit(UserDirectoryWorkingCopy wc) { + try { + // delete + for (LdapName dn : wc.getDeletedUsers().keySet()) { + getLdapContext().destroySubcontext(dn); + } + // add + for (LdapName dn : wc.getNewUsers().keySet()) { + DirectoryUser user = wc.getNewUsers().get(dn); + getLdapContext().createSubcontext(dn, user.getAttributes()); + } + // modify + for (LdapName dn : wc.getModifiedUsers().keySet()) { + Attributes modifiedAttrs = wc.getModifiedUsers().get(dn); + getLdapContext().modifyAttributes(dn, DirContext.REPLACE_ATTRIBUTE, modifiedAttrs); + } + } catch (NamingException e) { + throw new UserDirectoryException("Cannot commit LDAP", e); + } + } + + @Override + protected void rollback(UserDirectoryWorkingCopy wc) { + // prepare not impacting + } + +} diff --git a/org.argeo.enterprise/src/org/argeo/osgi/useradmin/LdifAuthorization.java b/org.argeo.enterprise/src/org/argeo/osgi/useradmin/LdifAuthorization.java new file mode 100644 index 000000000..e06c42e1f --- /dev/null +++ b/org.argeo.enterprise/src/org/argeo/osgi/useradmin/LdifAuthorization.java @@ -0,0 +1,82 @@ +package org.argeo.osgi.useradmin; + +import java.util.Arrays; +import java.util.Collections; +import java.util.Dictionary; +import java.util.List; + +import org.osgi.service.useradmin.Authorization; +import org.osgi.service.useradmin.Role; +import org.osgi.service.useradmin.User; + +/** Basic authorization. */ +class LdifAuthorization implements Authorization { + private final String name; + private final String displayName; + private final List allRoles; + + @SuppressWarnings("unchecked") + public LdifAuthorization(User user, List allRoles) { + if (user == null) { + this.name = null; + this.displayName = "anonymous"; + } else { + this.name = user.getName(); + Dictionary props = user.getProperties(); + Object displayName = props.get(LdifName.displayName); + if (displayName == null) + displayName = props.get(LdifName.cn); + if (displayName == null) + displayName = props.get(LdifName.uid); + if (displayName == null) + displayName = user.getName(); + if (displayName == null) + throw new UserDirectoryException("Cannot set display name for " + + user); + this.displayName = displayName.toString(); + } + // roles + String[] roles = new String[allRoles.size()]; + for (int i = 0; i < allRoles.size(); i++) { + roles[i] = allRoles.get(i).getName(); + } + this.allRoles = Collections.unmodifiableList(Arrays.asList(roles)); + } + + @Override + public String getName() { + return name; + } + + @Override + public boolean hasRole(String name) { + return allRoles.contains(name); + } + + @Override + public String[] getRoles() { + return allRoles.toArray(new String[allRoles.size()]); + } + + @Override + public int hashCode() { + if (name == null) + return super.hashCode(); + return name.hashCode(); + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof Authorization)) + return false; + Authorization that = (Authorization) obj; + if (name == null) + return that.getName() == null; + return name.equals(that.getName()); + } + + @Override + public String toString() { + return displayName; + } +} diff --git a/org.argeo.enterprise/src/org/argeo/osgi/useradmin/LdifGroup.java b/org.argeo.enterprise/src/org/argeo/osgi/useradmin/LdifGroup.java new file mode 100644 index 000000000..bd129119b --- /dev/null +++ b/org.argeo.enterprise/src/org/argeo/osgi/useradmin/LdifGroup.java @@ -0,0 +1,106 @@ +package org.argeo.osgi.useradmin; + +import java.util.ArrayList; +import java.util.List; + +import javax.naming.NamingEnumeration; +import javax.naming.directory.Attribute; +import javax.naming.directory.Attributes; +import javax.naming.ldap.LdapName; + +import org.osgi.service.useradmin.Role; + +/** Directory group implementation */ +class LdifGroup extends LdifUser implements DirectoryGroup { + private final String memberAttributeId; + + LdifGroup(AbstractUserDirectory userAdmin, LdapName dn, + Attributes attributes) { + super(userAdmin, dn, attributes); + memberAttributeId = userAdmin.getMemberAttributeId(); + } + + @Override + public boolean addMember(Role role) { + getUserAdmin().checkEdit(); + if (!isEditing()) + startEditing(); + + Attribute member = getAttributes().get(memberAttributeId); + if (member != null) { + if (member.contains(role.getName())) + return false; + else + member.add(role.getName()); + } else + getAttributes().put(memberAttributeId, role.getName()); + return true; + } + + @Override + public boolean addRequiredMember(Role role) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean removeMember(Role role) { + getUserAdmin().checkEdit(); + if (!isEditing()) + startEditing(); + + Attribute member = getAttributes().get(memberAttributeId); + if (member != null) { + if (!member.contains(role.getName())) + return false; + member.remove(role.getName()); + return true; + } else + return false; + } + + @Override + public Role[] getMembers() { + List directMembers = new ArrayList(); + for (LdapName ldapName : getMemberNames()) { + Role role = getUserAdmin().getRole(ldapName.toString()); + if (role == null) { + if (getUserAdmin().getExternalRoles() != null) + role = getUserAdmin().getExternalRoles().getRole( + ldapName.toString()); + } + if (role == null) + throw new UserDirectoryException("No role found for " + + ldapName); + directMembers.add(role); + } + return directMembers.toArray(new Role[directMembers.size()]); + } + + @Override + public List getMemberNames() { + Attribute memberAttribute = getAttributes().get(memberAttributeId); + if (memberAttribute == null) + return new ArrayList(); + try { + List roles = new ArrayList(); + NamingEnumeration values = memberAttribute.getAll(); + while (values.hasMore()) { + LdapName dn = new LdapName(values.next().toString()); + roles.add(dn); + } + return roles; + } catch (Exception e) { + throw new UserDirectoryException("Cannot get members", e); + } + } + + @Override + public Role[] getRequiredMembers() { + throw new UnsupportedOperationException(); + } + + @Override + public int getType() { + return GROUP; + } +} diff --git a/org.argeo.enterprise/src/org/argeo/osgi/useradmin/LdifName.java b/org.argeo.enterprise/src/org/argeo/osgi/useradmin/LdifName.java new file mode 100644 index 000000000..919e5073f --- /dev/null +++ b/org.argeo.enterprise/src/org/argeo/osgi/useradmin/LdifName.java @@ -0,0 +1,26 @@ +package org.argeo.osgi.useradmin; + +import javax.naming.ldap.LdapName; + +/** + * Standard LDAP attributes and object classes leveraged in this implementation + * of user admin. Named {@link LdifName} in order not to collide with + * {@link LdapName}. + */ +public enum LdifName { + // Attributes + dn, dc, cn, sn, uid, mail, displayName, objectClass, userPassword, givenName, description, member, + // Object classes + inetOrgPerson, organizationalPerson, person, groupOfNames, groupOfUniqueNames, top; + + public final static String PREFIX = "ldap:"; + + /** For use as XML name. */ + public String property() { + return PREFIX + name(); + } + + public static LdifName local(String property) { + return LdifName.valueOf(property.substring(PREFIX.length())); + } +} diff --git a/org.argeo.enterprise/src/org/argeo/osgi/useradmin/LdifUser.java b/org.argeo.enterprise/src/org/argeo/osgi/useradmin/LdifUser.java new file mode 100644 index 000000000..866c48c6b --- /dev/null +++ b/org.argeo.enterprise/src/org/argeo/osgi/useradmin/LdifUser.java @@ -0,0 +1,363 @@ +package org.argeo.osgi.useradmin; + +import java.io.UnsupportedEncodingException; +import java.nio.ByteBuffer; +import java.nio.CharBuffer; +import java.nio.charset.Charset; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Base64; +import java.util.Collections; +import java.util.Dictionary; +import java.util.Enumeration; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Set; + +import javax.naming.NamingEnumeration; +import javax.naming.NamingException; +import javax.naming.directory.Attribute; +import javax.naming.directory.Attributes; +import javax.naming.directory.BasicAttribute; +import javax.naming.ldap.LdapName; + +/** Directory user implementation */ +class LdifUser implements DirectoryUser { + private final AbstractUserDirectory userAdmin; + + private final LdapName dn; + + private final boolean frozen; + private Attributes publishedAttributes; + + private final AttributeDictionary properties; + private final AttributeDictionary credentials; + + LdifUser(AbstractUserDirectory userAdmin, LdapName dn, Attributes attributes) { + this(userAdmin, dn, attributes, false); + } + + private LdifUser(AbstractUserDirectory userAdmin, LdapName dn, Attributes attributes, boolean frozen) { + this.userAdmin = userAdmin; + this.dn = dn; + this.publishedAttributes = attributes; + properties = new AttributeDictionary(false); + credentials = new AttributeDictionary(true); + this.frozen = frozen; + } + + @Override + public String getName() { + return dn.toString(); + } + + @Override + public int getType() { + return USER; + } + + @Override + public Dictionary getProperties() { + return properties; + } + + @Override + public Dictionary getCredentials() { + return credentials; + } + + @Override + public boolean hasCredential(String key, Object value) { + if (key == null) { + // TODO check other sources (like PKCS12) + char[] password = toChars(value); + byte[] hashedPassword = hash(password); + return hasCredential(LdifName.userPassword.name(), hashedPassword); + } + + Object storedValue = getCredentials().get(key); + if (storedValue == null || value == null) + return false; + if (!(value instanceof String || value instanceof byte[])) + return false; + if (storedValue instanceof String && value instanceof String) + return storedValue.equals(value); + if (storedValue instanceof byte[] && value instanceof byte[]) + return Arrays.equals((byte[]) storedValue, (byte[]) value); + return false; + } + + /** Hash and clear the password */ + private byte[] hash(char[] password) { + byte[] hashedPassword = ("{SHA}" + Base64.getEncoder().encodeToString(DigestUtils.sha1(toBytes(password)))) + .getBytes(); + Arrays.fill(password, '\u0000'); + return hashedPassword; + } + + private byte[] toBytes(char[] chars) { + CharBuffer charBuffer = CharBuffer.wrap(chars); + ByteBuffer byteBuffer = Charset.forName("UTF-8").encode(charBuffer); + byte[] bytes = Arrays.copyOfRange(byteBuffer.array(), byteBuffer.position(), byteBuffer.limit()); + Arrays.fill(charBuffer.array(), '\u0000'); // clear sensitive data + Arrays.fill(byteBuffer.array(), (byte) 0); // clear sensitive data + return bytes; + } + + private char[] toChars(Object obj) { + if (obj instanceof char[]) + return (char[]) obj; + if (!(obj instanceof byte[])) + throw new IllegalArgumentException(obj.getClass() + " is not a byte array"); + ByteBuffer fromBuffer = ByteBuffer.wrap((byte[]) obj); + CharBuffer toBuffer = Charset.forName("UTF-8").decode(fromBuffer); + char[] res = Arrays.copyOfRange(toBuffer.array(), toBuffer.position(), toBuffer.limit()); + Arrays.fill(fromBuffer.array(), (byte) 0); // clear sensitive data + Arrays.fill((byte[]) obj, (byte) 0); // clear sensitive data + Arrays.fill(toBuffer.array(), '\u0000'); // clear sensitive data + return res; + } + + @Override + public LdapName getDn() { + return dn; + } + + @Override + public synchronized Attributes getAttributes() { + return isEditing() ? getModifiedAttributes() : publishedAttributes; + } + + /** Should only be called from working copy thread. */ + private synchronized Attributes getModifiedAttributes() { + assert getWc() != null; + return getWc().getAttributes(getDn()); + } + + protected synchronized boolean isEditing() { + return getWc() != null && getModifiedAttributes() != null; + } + + private synchronized UserDirectoryWorkingCopy getWc() { + return userAdmin.getWorkingCopy(); + } + + protected synchronized void startEditing() { + if (frozen) + throw new UserDirectoryException("Cannot edit frozen view"); + if (getUserAdmin().isReadOnly()) + throw new UserDirectoryException("User directory is read-only"); + assert getModifiedAttributes() == null; + getWc().startEditing(this); + // modifiedAttributes = (Attributes) publishedAttributes.clone(); + } + + public synchronized void publishAttributes(Attributes modifiedAttributes) { + publishedAttributes = modifiedAttributes; + } + + public DirectoryUser getPublished() { + return new LdifUser(userAdmin, dn, publishedAttributes, true); + } + + @Override + public int hashCode() { + return dn.hashCode(); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj instanceof LdifUser) { + LdifUser that = (LdifUser) obj; + return this.dn.equals(that.dn); + } + return false; + } + + @Override + public String toString() { + return dn.toString(); + } + + protected AbstractUserDirectory getUserAdmin() { + return userAdmin; + } + + private class AttributeDictionary extends Dictionary { + private final List effectiveKeys = new ArrayList(); + private final List attrFilter; + private final Boolean includeFilter; + + public AttributeDictionary(Boolean includeFilter) { + this.attrFilter = userAdmin.getCredentialAttributeIds(); + this.includeFilter = includeFilter; + try { + NamingEnumeration ids = getAttributes().getIDs(); + while (ids.hasMore()) { + String id = ids.next(); + if (includeFilter && attrFilter.contains(id)) + effectiveKeys.add(id); + else if (!includeFilter && !attrFilter.contains(id)) + effectiveKeys.add(id); + } + } catch (NamingException e) { + throw new UserDirectoryException("Cannot initialise attribute dictionary", e); + } + } + + @Override + public int size() { + return effectiveKeys.size(); + } + + @Override + public boolean isEmpty() { + return effectiveKeys.size() == 0; + } + + @Override + public Enumeration keys() { + return Collections.enumeration(effectiveKeys); + } + + @Override + public Enumeration elements() { + final Iterator it = effectiveKeys.iterator(); + return new Enumeration() { + + @Override + public boolean hasMoreElements() { + return it.hasNext(); + } + + @Override + public Object nextElement() { + String key = it.next(); + return get(key); + } + + }; + } + + @Override + public Object get(Object key) { + try { + Attribute attr = getAttributes().get(key.toString()); + if (attr == null) + return null; + Object value = attr.get(); + if (value instanceof byte[]) { + if (key.equals(LdifName.userPassword.name())) + // TODO other cases (certificates, images) + return value; + value = new String((byte[]) value, Charset.forName("UTF-8")); + } + if (attr.size() == 1) + return value; + if (!attr.getID().equals(LdifName.objectClass.name())) + return value; + // special case for object class + NamingEnumeration en = attr.getAll(); + Set objectClasses = new HashSet(); + while (en.hasMore()) { + String objectClass = en.next().toString(); + objectClasses.add(objectClass); + } + + if (objectClasses.contains(userAdmin.getUserObjectClass())) + return userAdmin.getUserObjectClass(); + else if (objectClasses.contains(userAdmin.getGroupObjectClass())) + return userAdmin.getGroupObjectClass(); + else + return value; + } catch (NamingException e) { + throw new UserDirectoryException("Cannot get value for attribute " + key, e); + } + } + + @Override + public Object put(String key, Object value) { + if (key == null) { + // TODO persist to other sources (like PKCS12) + char[] password = toChars(value); + byte[] hashedPassword = hash(password); + return put(LdifName.userPassword.name(), hashedPassword); + } + + userAdmin.checkEdit(); + if (!isEditing()) + startEditing(); + + if (!(value instanceof String || value instanceof byte[])) + throw new IllegalArgumentException("Value must be String or byte[]"); + + if (includeFilter && !attrFilter.contains(key)) + throw new IllegalArgumentException("Key " + key + " not included"); + else if (!includeFilter && attrFilter.contains(key)) + throw new IllegalArgumentException("Key " + key + " excluded"); + + try { + Attribute attribute = getModifiedAttributes().get(key.toString()); + attribute = new BasicAttribute(key.toString()); + if (value instanceof String && !isAsciiPrintable(((String) value))) + try { + attribute.add(((String) value).getBytes("UTF-8")); + } catch (UnsupportedEncodingException e) { + throw new UserDirectoryException("Cannot encode " + value, e); + } + else + attribute.add(value); + Attribute previousAttribute = getModifiedAttributes().put(attribute); + if (previousAttribute != null) + return previousAttribute.get(); + else + return null; + } catch (NamingException e) { + throw new UserDirectoryException("Cannot get value for attribute " + key, e); + } + } + + @Override + public Object remove(Object key) { + userAdmin.checkEdit(); + if (!isEditing()) + startEditing(); + + if (includeFilter && !attrFilter.contains(key)) + throw new IllegalArgumentException("Key " + key + " not included"); + else if (!includeFilter && attrFilter.contains(key)) + throw new IllegalArgumentException("Key " + key + " excluded"); + + try { + Attribute attr = getModifiedAttributes().remove(key.toString()); + if (attr != null) + return attr.get(); + else + return null; + } catch (NamingException e) { + throw new UserDirectoryException("Cannot remove attribute " + key, e); + } + } + } + + private static boolean isAsciiPrintable(String str) { + if (str == null) { + return false; + } + int sz = str.length(); + for (int i = 0; i < sz; i++) { + if (isAsciiPrintable(str.charAt(i)) == false) { + return false; + } + } + return true; + } + + private static boolean isAsciiPrintable(char ch) { + return ch >= 32 && ch < 127; + } + +} diff --git a/org.argeo.enterprise/src/org/argeo/osgi/useradmin/LdifUserAdmin.java b/org.argeo.enterprise/src/org/argeo/osgi/useradmin/LdifUserAdmin.java new file mode 100644 index 000000000..521ae8bb6 --- /dev/null +++ b/org.argeo.enterprise/src/org/argeo/osgi/useradmin/LdifUserAdmin.java @@ -0,0 +1,238 @@ +package org.argeo.osgi.useradmin; + +import static org.argeo.osgi.useradmin.LdifName.inetOrgPerson; +import static org.argeo.osgi.useradmin.LdifName.objectClass; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.Dictionary; +import java.util.HashSet; +import java.util.Hashtable; +import java.util.List; +import java.util.Set; +import java.util.SortedMap; +import java.util.TreeMap; + +import javax.naming.NamingEnumeration; +import javax.naming.directory.Attributes; +import javax.naming.ldap.LdapName; +import javax.transaction.TransactionManager; + +import org.argeo.util.naming.LdifParser; +import org.argeo.util.naming.LdifWriter; +import org.osgi.framework.Filter; +import org.osgi.service.useradmin.Role; + +/** + * A user admin based on a LDIF files. Requires a {@link TransactionManager} and + * an open transaction for write access. + */ +public class LdifUserAdmin extends AbstractUserDirectory { + private SortedMap users = new TreeMap(); + private SortedMap groups = new TreeMap(); + + public LdifUserAdmin(String uri, String baseDn) { + this(fromUri(uri, baseDn)); + } + + public LdifUserAdmin(Dictionary properties) { + super(properties); + } + + public LdifUserAdmin(InputStream in) { + super(new Hashtable()); + load(in); + } + + private static Dictionary fromUri(String uri, String baseDn) { + Hashtable res = new Hashtable(); + res.put(UserAdminConf.uri.name(), uri); + res.put(UserAdminConf.baseDn.name(), baseDn); + return res; + } + + public void init() { + try { + if (getUri().getScheme().equals("file")) { + File file = new File(getUri()); + if (!file.exists()) + return; + } + load(getUri().toURL().openStream()); + } catch (Exception e) { + throw new UserDirectoryException("Cannot open URL " + getUri(), e); + } + } + + public void save() { + if (getUri() == null) + throw new UserDirectoryException("Cannot save LDIF user admin: no URI is set"); + if (isReadOnly()) + throw new UserDirectoryException("Cannot save LDIF user admin: " + getUri() + " is read-only"); + try (FileOutputStream out = new FileOutputStream(new File(getUri()))) { + save(out); + } catch (IOException e) { + throw new UserDirectoryException("Cannot save user admin to " + getUri(), e); + } + } + + public void save(OutputStream out) throws IOException { + try { + LdifWriter ldifWriter = new LdifWriter(out); + for (LdapName name : groups.keySet()) + ldifWriter.writeEntry(name, groups.get(name).getAttributes()); + for (LdapName name : users.keySet()) + ldifWriter.writeEntry(name, users.get(name).getAttributes()); + } finally { + out.close(); + } + } + + protected void load(InputStream in) { + try { + users.clear(); + groups.clear(); + + LdifParser ldifParser = new LdifParser(); + SortedMap allEntries = ldifParser.read(in); + for (LdapName key : allEntries.keySet()) { + Attributes attributes = allEntries.get(key); + // check for inconsistency + Set lowerCase = new HashSet(); + NamingEnumeration ids = attributes.getIDs(); + while (ids.hasMoreElements()) { + String id = ids.nextElement().toLowerCase(); + if (lowerCase.contains(id)) + throw new UserDirectoryException(key + " has duplicate id " + id); + lowerCase.add(id); + } + + // analyse object classes + NamingEnumeration objectClasses = attributes.get(objectClass.name()).getAll(); + // System.out.println(key); + objectClasses: while (objectClasses.hasMore()) { + String objectClass = objectClasses.next().toString(); + // System.out.println(" " + objectClass); + if (objectClass.equals(inetOrgPerson.name())) { + users.put(key, new LdifUser(this, key, attributes)); + break objectClasses; + } else if (objectClass.equals(getGroupObjectClass())) { + groups.put(key, new LdifGroup(this, key, attributes)); + break objectClasses; + } + } + } + } catch (Exception e) { + throw new UserDirectoryException("Cannot load user admin service from LDIF", e); + } + } + + public void destroy() { + if (users == null || groups == null) + throw new UserDirectoryException("User directory " + getBaseDn() + " is already destroyed"); + users.clear(); + users = null; + groups.clear(); + groups = null; + } + + protected DirectoryUser daoGetRole(LdapName key) { + if (groups.containsKey(key)) + return groups.get(key); + if (users.containsKey(key)) + return users.get(key); + return null; + } + + protected Boolean daoHasRole(LdapName dn) { + return users.containsKey(dn) || groups.containsKey(dn); + } + + @SuppressWarnings("unchecked") + protected List doGetRoles(Filter f) { + ArrayList res = new ArrayList(); + if (f == null) { + res.addAll(users.values()); + res.addAll(groups.values()); + } else { + for (DirectoryUser user : users.values()) { + // System.out.println("\n" + user.getName()); + // Dictionary props = user.getProperties(); + // for (Enumeration keys = props.keys(); keys + // .hasMoreElements();) { + // String key = keys.nextElement(); + // System.out.println(" " + key + "=" + props.get(key)); + // } + if (f.match(user.getProperties())) + res.add(user); + } + for (DirectoryUser group : groups.values()) + if (f.match(group.getProperties())) + res.add(group); + } + return res; + } + + @Override + protected List getDirectGroups(LdapName dn) { + List directGroups = new ArrayList(); + for (LdapName name : groups.keySet()) { + DirectoryGroup group = groups.get(name); + if (group.getMemberNames().contains(dn)) + directGroups.add(group.getDn()); + } + return directGroups; + } + + @Override + protected void prepare(UserDirectoryWorkingCopy wc) { + // delete + for (LdapName dn : wc.getDeletedUsers().keySet()) { + if (users.containsKey(dn)) + users.remove(dn); + else if (groups.containsKey(dn)) + groups.remove(dn); + else + throw new UserDirectoryException("User to delete not found " + dn); + } + // add + for (LdapName dn : wc.getNewUsers().keySet()) { + DirectoryUser user = wc.getNewUsers().get(dn); + if (users.containsKey(dn) || groups.containsKey(dn)) + throw new UserDirectoryException("User to create found " + dn); + else if (Role.USER == user.getType()) + users.put(dn, user); + else if (Role.GROUP == user.getType()) + groups.put(dn, (DirectoryGroup) user); + else + throw new UserDirectoryException("Unsupported role type " + user.getType() + " for new user " + dn); + } + // modify + for (LdapName dn : wc.getModifiedUsers().keySet()) { + Attributes modifiedAttrs = wc.getModifiedUsers().get(dn); + DirectoryUser user; + if (users.containsKey(dn)) + user = users.get(dn); + else if (groups.containsKey(dn)) + user = groups.get(dn); + else + throw new UserDirectoryException("User to modify no found " + dn); + user.publishAttributes(modifiedAttrs); + } + } + + @Override + protected void commit(UserDirectoryWorkingCopy wc) { + save(); + } + + @Override + protected void rollback(UserDirectoryWorkingCopy wc) { + init(); + } + +} diff --git a/org.argeo.enterprise/src/org/argeo/osgi/useradmin/UserAdminConf.java b/org.argeo.enterprise/src/org/argeo/osgi/useradmin/UserAdminConf.java new file mode 100644 index 000000000..316941ea8 --- /dev/null +++ b/org.argeo.enterprise/src/org/argeo/osgi/useradmin/UserAdminConf.java @@ -0,0 +1,193 @@ +package org.argeo.osgi.useradmin; + +import java.io.UnsupportedEncodingException; +import java.net.URI; +import java.net.URISyntaxException; +import java.net.URLDecoder; +import java.util.Dictionary; +import java.util.Enumeration; +import java.util.Hashtable; +import java.util.LinkedHashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; + +import javax.naming.Context; + +import org.osgi.framework.Constants; + +/** Properties used to configure user admins. */ +public enum UserAdminConf { + /** Base DN (cannot be configured externally) */ + baseDn("dc=example,dc=com"), + + /** URI of the underlying resource (cannot be configured externally) */ + uri("ldap://localhost:10389"), + + /** User objectClass */ + userObjectClass("inetOrgPerson"), + + /** Relative base DN for users */ + userBase("ou=People"), + + /** Groups objectClass */ + groupObjectClass("groupOfNames"), + + /** Relative base DN for users */ + groupBase("ou=Groups"), + + /** Read-only source */ + readOnly(null); + + public final static String FACTORY_PID = "org.argeo.osgi.useradmin.config"; + + /** The default value. */ + private Object def; + + UserAdminConf(Object def) { + this.def = def; + } + + public Object getDefault() { + return def; + } + + /** + * For use as Java property. + * + * @deprecated use {@link #name()} instead + */ + @Deprecated + public String property() { + return name(); + } + + public String getValue(Dictionary properties) { + Object res = getRawValue(properties); + if (res == null) + return null; + return res.toString(); + } + + @SuppressWarnings("unchecked") + public T getRawValue(Dictionary properties) { + Object res = properties.get(name()); + if (res == null) + res = getDefault(); + return (T) res; + } + + /** @deprecated use {@link #valueOf(String)} instead */ + @Deprecated + public static UserAdminConf local(String property) { + return UserAdminConf.valueOf(property); + } + + /** Hides host and credentials. */ + public static URI propertiesAsUri(Dictionary properties) { + StringBuilder query = new StringBuilder(); + + boolean first = true; + for (Enumeration keys = properties.keys(); keys.hasMoreElements();) { + String key = keys.nextElement(); + // TODO clarify which keys are relevant (list only the enum?) + if (!key.equals("service.factoryPid") && !key.equals("cn") && !key.equals("dn") + && !key.equals(Constants.SERVICE_PID) && !key.startsWith("java") && !key.equals(baseDn.name()) + && !key.equals(uri.name())) { + if (first) + first = false; + else + query.append('&'); + query.append(valueOf(key).name()); + query.append('=').append(properties.get(key).toString()); + } + } + + String bDn = (String) properties.get(baseDn.name()); + try { + return new URI(null, null, bDn != null ? '/' + bDn : null, query.length() != 0 ? query.toString() : null, + null); + } catch (URISyntaxException e) { + throw new UserDirectoryException("Cannot create URI from properties", e); + } + } + + public static Dictionary uriAsProperties(String uriStr) { + try { + Hashtable res = new Hashtable(); + URI u = new URI(uriStr); + String scheme = u.getScheme(); + String path = u.getPath(); + String bDn = path.substring(path.lastIndexOf('/') + 1, path.length()); + if (bDn.endsWith(".ldif")) + bDn = bDn.substring(0, bDn.length() - ".ldif".length()); + + String principal = null; + String credentials = null; + if (scheme != null) + if (scheme.equals("ldap") || scheme.equals("ldaps")) { + // TODO additional checks + String[] userInfo = u.getUserInfo().split(":"); + principal = userInfo.length > 0 ? userInfo[0] : null; + credentials = userInfo.length > 1 ? userInfo[1] : null; + } else if (scheme.equals("file")) { + } else + throw new UserDirectoryException("Unsupported scheme " + scheme); + Map> query = splitQuery(u.getQuery()); + for (String key : query.keySet()) { + UserAdminConf ldapProp = UserAdminConf.valueOf(key); + List values = query.get(key); + if (values.size() == 1) { + res.put(ldapProp.name(), values.get(0)); + } else { + throw new UserDirectoryException("Only single values are supported"); + } + } + res.put(baseDn.name(), bDn); + if (principal != null) + res.put(Context.SECURITY_PRINCIPAL, principal); + if (credentials != null) + res.put(Context.SECURITY_CREDENTIALS, credentials); + if (scheme != null) { + URI bareUri = new URI(scheme, null, u.getHost(), u.getPort(), + scheme.equals("file") ? u.getPath() : null, null, null); + res.put(uri.name(), bareUri.toString()); + } + return res; + } catch (Exception e) { + throw new UserDirectoryException("Cannot convert " + uri + " to properties", e); + } + } + + private static Map> splitQuery(String query) throws UnsupportedEncodingException { + final Map> query_pairs = new LinkedHashMap>(); + if (query == null) + return query_pairs; + final String[] pairs = query.split("&"); + for (String pair : pairs) { + final int idx = pair.indexOf("="); + final String key = idx > 0 ? URLDecoder.decode(pair.substring(0, idx), "UTF-8") : pair; + if (!query_pairs.containsKey(key)) { + query_pairs.put(key, new LinkedList()); + } + final String value = idx > 0 && pair.length() > idx + 1 + ? URLDecoder.decode(pair.substring(idx + 1), "UTF-8") : null; + query_pairs.get(key).add(value); + } + return query_pairs; + } + + public static void main(String[] args) { + Dictionary props = uriAsProperties("ldap://" + "uid=admin,ou=system:secret@localhost:10389" + + "/dc=example,dc=com" + "?readOnly=false&userObjectClass=person"); + System.out.println(props); + System.out.println(propertiesAsUri(props)); + + System.out.println(uriAsProperties("file://some/dir/dc=example,dc=com.ldif")); + + props = uriAsProperties( + "/dc=example,dc=com.ldif?readOnly=true" + "&userBase=ou=CoWorkers,ou=People&groupBase=ou=Roles"); + System.out.println(props); + System.out.println(propertiesAsUri(props)); + } +} diff --git a/org.argeo.enterprise/src/org/argeo/osgi/useradmin/UserDirectory.java b/org.argeo.enterprise/src/org/argeo/osgi/useradmin/UserDirectory.java new file mode 100644 index 000000000..e5de73836 --- /dev/null +++ b/org.argeo.enterprise/src/org/argeo/osgi/useradmin/UserDirectory.java @@ -0,0 +1,23 @@ +package org.argeo.osgi.useradmin; + +import javax.naming.ldap.LdapName; +import javax.transaction.xa.XAResource; + +/** Information about a user directory. */ +public interface UserDirectory { + /** The base DN of all entries in this user directory */ + public LdapName getBaseDn(); + + /** The related {@link XAResource} */ + public XAResource getXaResource(); + + public boolean isReadOnly(); + + public String getUserObjectClass(); + + public String getUserBase(); + + public String getGroupObjectClass(); + + public String getGroupBase(); +} diff --git a/org.argeo.enterprise/src/org/argeo/osgi/useradmin/UserDirectoryException.java b/org.argeo.enterprise/src/org/argeo/osgi/useradmin/UserDirectoryException.java new file mode 100644 index 000000000..613d0fdf0 --- /dev/null +++ b/org.argeo.enterprise/src/org/argeo/osgi/useradmin/UserDirectoryException.java @@ -0,0 +1,19 @@ +package org.argeo.osgi.useradmin; + +import org.osgi.service.useradmin.UserAdmin; + +/** + * Exceptions related to Argeo's implementation of OSGi {@link UserAdmin} + * service. + */ +public class UserDirectoryException extends RuntimeException { + private static final long serialVersionUID = 1419352360062048603L; + + public UserDirectoryException(String message) { + super(message); + } + + public UserDirectoryException(String message, Throwable e) { + super(message, e); + } +} diff --git a/org.argeo.enterprise/src/org/argeo/osgi/useradmin/UserDirectoryWorkingCopy.java b/org.argeo.enterprise/src/org/argeo/osgi/useradmin/UserDirectoryWorkingCopy.java new file mode 100644 index 000000000..0e25bdfa1 --- /dev/null +++ b/org.argeo.enterprise/src/org/argeo/osgi/useradmin/UserDirectoryWorkingCopy.java @@ -0,0 +1,58 @@ +package org.argeo.osgi.useradmin; + +import java.util.HashMap; +import java.util.Map; + +import javax.naming.directory.Attributes; +import javax.naming.ldap.LdapName; +import javax.transaction.xa.XAResource; + +/** {@link XAResource} for a user directory being edited. */ +class UserDirectoryWorkingCopy { + // private final static Log log = LogFactory + // .getLog(UserDirectoryWorkingCopy.class); + + private Map newUsers = new HashMap(); + private Map modifiedUsers = new HashMap(); + private Map deletedUsers = new HashMap(); + + void cleanUp() { + // clean collections + newUsers.clear(); + newUsers = null; + modifiedUsers.clear(); + modifiedUsers = null; + deletedUsers.clear(); + deletedUsers = null; + } + + public boolean noModifications() { + return newUsers.size() == 0 && modifiedUsers.size() == 0 + && deletedUsers.size() == 0; + } + + public Attributes getAttributes(LdapName dn) { + if (modifiedUsers.containsKey(dn)) + return modifiedUsers.get(dn); + return null; + } + + public void startEditing(DirectoryUser user) { + LdapName dn = user.getDn(); + if (modifiedUsers.containsKey(dn)) + throw new UserDirectoryException("Already editing " + dn); + modifiedUsers.put(dn, (Attributes) user.getAttributes().clone()); + } + + public Map getNewUsers() { + return newUsers; + } + + public Map getDeletedUsers() { + return deletedUsers; + } + + public Map getModifiedUsers() { + return modifiedUsers; + } +} diff --git a/org.argeo.enterprise/src/org/argeo/osgi/useradmin/WcXaResource.java b/org.argeo.enterprise/src/org/argeo/osgi/useradmin/WcXaResource.java new file mode 100644 index 000000000..a6048fdca --- /dev/null +++ b/org.argeo.enterprise/src/org/argeo/osgi/useradmin/WcXaResource.java @@ -0,0 +1,143 @@ +package org.argeo.osgi.useradmin; + +import java.util.HashMap; +import java.util.Map; + +import javax.transaction.xa.XAException; +import javax.transaction.xa.XAResource; +import javax.transaction.xa.Xid; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** {@link XAResource} for a user directory being edited. */ +class WcXaResource implements XAResource { + private final static Log log = LogFactory.getLog(WcXaResource.class); + + private final AbstractUserDirectory userDirectory; + + private Map workingCopies = new HashMap(); + private Xid editingXid = null; + private int transactionTimeout = 0; + + public WcXaResource(AbstractUserDirectory userDirectory) { + this.userDirectory = userDirectory; + } + + @Override + public synchronized void start(Xid xid, int flags) throws XAException { + if (editingXid != null) + throw new UserDirectoryException("Already editing " + editingXid); + UserDirectoryWorkingCopy wc = workingCopies.put(xid, + new UserDirectoryWorkingCopy()); + if (wc != null) + throw new UserDirectoryException( + "There is already a working copy for " + xid); + this.editingXid = xid; + } + + @Override + public void end(Xid xid, int flags) throws XAException { + checkXid(xid); + } + + private UserDirectoryWorkingCopy wc(Xid xid) { + return workingCopies.get(xid); + } + + synchronized UserDirectoryWorkingCopy wc() { + if (editingXid == null) + return null; + UserDirectoryWorkingCopy wc = workingCopies.get(editingXid); + if (wc == null) + throw new UserDirectoryException("No working copy found for " + + editingXid); + return wc; + } + + private synchronized void cleanUp(Xid xid) { + wc(xid).cleanUp(); + workingCopies.remove(xid); + editingXid = null; + } + + @Override + public int prepare(Xid xid) throws XAException { + checkXid(xid); + UserDirectoryWorkingCopy wc = wc(xid); + if (wc.noModifications()) + return XA_RDONLY; + try { + userDirectory.prepare(wc); + } catch (Exception e) { + log.error("Cannot prepare " + xid, e); + throw new XAException(XAException.XAER_RMERR); + } + return XA_OK; + } + + @Override + public void commit(Xid xid, boolean onePhase) throws XAException { + try { + checkXid(xid); + UserDirectoryWorkingCopy wc = wc(xid); + if (wc.noModifications()) + return; + if (onePhase) + userDirectory.prepare(wc); + userDirectory.commit(wc); + } catch (Exception e) { + log.error("Cannot commit " + xid, e); + throw new XAException(XAException.XAER_RMERR); + } finally { + cleanUp(xid); + } + } + + @Override + public void rollback(Xid xid) throws XAException { + try { + checkXid(xid); + userDirectory.rollback(wc(xid)); + } catch (Exception e) { + log.error("Cannot rollback " + xid, e); + throw new XAException(XAException.XAER_RMERR); + } finally { + cleanUp(xid); + } + } + + @Override + public void forget(Xid xid) throws XAException { + throw new UnsupportedOperationException(); + } + + @Override + public boolean isSameRM(XAResource xares) throws XAException { + return xares == this; + } + + @Override + public Xid[] recover(int flag) throws XAException { + return new Xid[0]; + } + + @Override + public int getTransactionTimeout() throws XAException { + return transactionTimeout; + } + + @Override + public boolean setTransactionTimeout(int seconds) throws XAException { + transactionTimeout = seconds; + return true; + } + + private void checkXid(Xid xid) throws XAException { + if (xid == null) + throw new XAException(XAException.XAER_OUTSIDE); + if (!xid.equals(xid)) + throw new XAException(XAException.XAER_NOTA); + } + +} diff --git a/org.argeo.enterprise/src/org/argeo/util/naming/AttributesDictionary.java b/org.argeo.enterprise/src/org/argeo/util/naming/AttributesDictionary.java new file mode 100644 index 000000000..c211e8e8a --- /dev/null +++ b/org.argeo.enterprise/src/org/argeo/util/naming/AttributesDictionary.java @@ -0,0 +1,171 @@ +package org.argeo.util.naming; + +import java.util.Dictionary; +import java.util.Enumeration; + +import javax.naming.NamingEnumeration; +import javax.naming.NamingException; +import javax.naming.directory.Attribute; +import javax.naming.directory.Attributes; +import javax.naming.directory.BasicAttribute; + +public class AttributesDictionary extends Dictionary { + private final Attributes attributes; + + /** The provided attributes is wrapped, not copied. */ + public AttributesDictionary(Attributes attributes) { + if (attributes == null) + throw new IllegalArgumentException("Attributes cannot be null"); + this.attributes = attributes; + } + + @Override + public int size() { + return attributes.size(); + } + + @Override + public boolean isEmpty() { + return attributes.size() == 0; + } + + @Override + public Enumeration keys() { + NamingEnumeration namingEnumeration = attributes.getIDs(); + return new Enumeration() { + + @Override + public boolean hasMoreElements() { + return namingEnumeration.hasMoreElements(); + } + + @Override + public String nextElement() { + return namingEnumeration.nextElement(); + } + + }; + } + + @Override + public Enumeration elements() { + NamingEnumeration namingEnumeration = attributes.getIDs(); + return new Enumeration() { + + @Override + public boolean hasMoreElements() { + return namingEnumeration.hasMoreElements(); + } + + @Override + public Object nextElement() { + String key = namingEnumeration.nextElement(); + return get(key); + } + + }; + } + + @Override + /** @returns a String or String[] */ + public Object get(Object key) { + try { + if (key == null) + throw new IllegalArgumentException("Key cannot be null"); + Attribute attr = attributes.get(key.toString()); + if (attr == null) + return null; + if (attr.size() == 0) + throw new IllegalStateException("There must be at least one value"); + else if (attr.size() == 1) { + return attr.get().toString(); + } else {// multiple + String[] res = new String[attr.size()]; + for (int i = 0; i < attr.size(); i++) { + Object value = attr.get(); + if (value == null) + throw new RuntimeException("Values cannot be null"); + res[i] = attr.get(i).toString(); + } + return res; + } + } catch (NamingException e) { + throw new RuntimeException("Cannot get value for " + key, e); + } + } + + @Override + public Object put(String key, Object value) { + if (key == null) + throw new IllegalArgumentException("Key cannot be null"); + if (value == null) + throw new IllegalArgumentException("Value cannot be null"); + + Object oldValue = get(key); + Attribute attr = attributes.get(key); + if (attr == null) { + attr = new BasicAttribute(key); + attributes.put(attr); + } + + if (value instanceof String[]) { + String[] values = (String[]) value; + // clean additional values + for (int i = values.length; i < attr.size(); i++) + attr.remove(i); + // set values + for (int i = 0; i < values.length; i++) { + attr.set(i, values[i]); + } + } else { + if (attr.size() > 1) + throw new IllegalArgumentException("Attribute " + key + " is multi-valued"); + if (attr.size() == 1) { + try { + if (!attr.get(0).equals(value)) + attr.set(0, value.toString()); + } catch (NamingException e) { + throw new RuntimeException("Cannot check existing value", e); + } + } else { + attr.add(value.toString()); + } + } + return oldValue; + } + + @Override + public Object remove(Object key) { + if (key == null) + throw new IllegalArgumentException("Key cannot be null"); + Object oldValue = get(key); + if (oldValue == null) + return null; + return attributes.remove(key.toString()); + } + + /** + * Copy the content of an {@link javax.naming.Attributes} to the + * provided {@link Dictionary}. + */ + public static void copy(Attributes attributes, Dictionary dictionary) { + AttributesDictionary ad = new AttributesDictionary(attributes); + Enumeration keys = ad.keys(); + while (keys.hasMoreElements()) { + String key = keys.nextElement(); + dictionary.put(key, ad.get(key)); + } + } + + /** + * Copy a {@link Dictionary} into an {@link javax.naming.Attributes}. + */ + public static void copy(Dictionary dictionary, Attributes attributes) { + AttributesDictionary ad = new AttributesDictionary(attributes); + Enumeration keys = dictionary.keys(); + while (keys.hasMoreElements()) { + String key = keys.nextElement(); + ad.put(key, dictionary.get(key)); + } + } +} diff --git a/org.argeo.enterprise/src/org/argeo/util/naming/LdifParser.java b/org.argeo.enterprise/src/org/argeo/util/naming/LdifParser.java new file mode 100644 index 000000000..ec73e8acc --- /dev/null +++ b/org.argeo.enterprise/src/org/argeo/util/naming/LdifParser.java @@ -0,0 +1,145 @@ +package org.argeo.util.naming; + +import static org.argeo.osgi.useradmin.LdifName.dn; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.util.ArrayList; +import java.util.Base64; +import java.util.List; +import java.util.SortedMap; +import java.util.TreeMap; + +import javax.naming.InvalidNameException; +import javax.naming.NamingException; +import javax.naming.directory.Attribute; +import javax.naming.directory.Attributes; +import javax.naming.directory.BasicAttribute; +import javax.naming.directory.BasicAttributes; +import javax.naming.ldap.LdapName; +import javax.naming.ldap.Rdn; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.argeo.osgi.useradmin.UserDirectoryException; + +/** Basic LDIF parser. */ +public class LdifParser { + private final static Log log = LogFactory.getLog(LdifParser.class); + + protected Attributes addAttributes(SortedMap res, int lineNumber, LdapName currentDn, + Attributes currentAttributes) { + try { + Rdn nameRdn = currentDn.getRdn(currentDn.size() - 1); + Attribute nameAttr = currentAttributes.get(nameRdn.getType()); + if (nameAttr == null) + currentAttributes.put(nameRdn.getType(), nameRdn.getValue()); + else if (!nameAttr.get().equals(nameRdn.getValue())) + throw new UserDirectoryException( + "Attribute " + nameAttr.getID() + "=" + nameAttr.get() + " not consistent with DN " + currentDn + + " (shortly before line " + lineNumber + " in LDIF file)"); + Attributes previous = res.put(currentDn, currentAttributes); + if (log.isTraceEnabled()) + log.trace("Added " + currentDn); + return previous; + } catch (NamingException e) { + throw new UserDirectoryException("Cannot add " + currentDn, e); + } + } + + public SortedMap read(InputStream in) throws IOException { + SortedMap res = new TreeMap(); + try { + List lines = new ArrayList<>(); + try (BufferedReader br = new BufferedReader(new InputStreamReader(in))) { + String line; + while ((line = br.readLine()) != null) { + lines.add(line); + } + } + if (lines.size() == 0) + return res; + // add an empty new line since the last line is not checked + if (!lines.get(lines.size() - 1).equals("")) + lines.add(""); + + LdapName currentDn = null; + Attributes currentAttributes = null; + StringBuilder currentEntry = new StringBuilder(); + + readLines: for (int lineNumber = 0; lineNumber < lines.size(); lineNumber++) { + String line = lines.get(lineNumber); + boolean isLastLine = false; + if (lineNumber == lines.size() - 1) + isLastLine = true; + if (line.startsWith(" ")) { + currentEntry.append(line.substring(1)); + if (!isLastLine) + continue readLines; + } + + if (currentEntry.length() != 0 || isLastLine) { + // read previous attribute + StringBuilder attrId = new StringBuilder(8); + boolean isBase64 = false; + readAttrId: for (int i = 0; i < currentEntry.length(); i++) { + char c = currentEntry.charAt(i); + if (c == ':') { + if (i + 1 < currentEntry.length() && currentEntry.charAt(i + 1) == ':') + isBase64 = true; + currentEntry.delete(0, i + (isBase64 ? 2 : 1)); + break readAttrId; + } else { + attrId.append(c); + } + } + + String attributeId = attrId.toString(); + String cleanValueStr = currentEntry.toString().trim(); + Object attributeValue = isBase64 ? Base64.getDecoder().decode(cleanValueStr) : cleanValueStr; + + // manage DN attributes + if (attributeId.equals(dn.name()) || isLastLine) { + if (currentDn != null) { + // + // ADD + // + Attributes previous = addAttributes(res, lineNumber, currentDn, currentAttributes); + if (previous != null) { + log.warn("There was already an entry with DN " + currentDn + + ", which has been discarded by a subsequent one."); + } + } + + if (attributeId.equals(dn.name())) + try { + currentDn = new LdapName(attributeValue.toString()); + currentAttributes = new BasicAttributes(true); + } catch (InvalidNameException e) { + log.error(attributeValue + " not a valid DN, skipping the entry."); + currentDn = null; + currentAttributes = null; + } + } + + // store attribute + if (currentAttributes != null) { + Attribute attribute = currentAttributes.get(attributeId); + if (attribute == null) { + attribute = new BasicAttribute(attributeId); + currentAttributes.put(attribute); + } + attribute.add(attributeValue); + } + currentEntry = new StringBuilder(); + } + currentEntry.append(line); + } + } finally { + in.close(); + } + return res; + } +} \ No newline at end of file diff --git a/org.argeo.enterprise/src/org/argeo/util/naming/LdifWriter.java b/org.argeo.enterprise/src/org/argeo/util/naming/LdifWriter.java new file mode 100644 index 000000000..37d90b4e4 --- /dev/null +++ b/org.argeo.enterprise/src/org/argeo/util/naming/LdifWriter.java @@ -0,0 +1,77 @@ +package org.argeo.util.naming; + +import static org.argeo.osgi.useradmin.LdifName.dn; + +import java.io.IOException; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.io.Writer; +import java.util.Base64; +import java.util.Map; + +import javax.naming.NamingEnumeration; +import javax.naming.NamingException; +import javax.naming.directory.Attribute; +import javax.naming.directory.Attributes; +import javax.naming.ldap.LdapName; +import javax.naming.ldap.Rdn; + +import org.argeo.osgi.useradmin.UserDirectoryException; + +/** Basic LDIF writer */ +public class LdifWriter { + private final Writer writer; + + /** Writer must be closed by caller */ + public LdifWriter(Writer writer) { + this.writer = writer; + } + + /** Stream must be closed by caller */ + public LdifWriter(OutputStream out) { + this(new OutputStreamWriter(out)); + } + + public void writeEntry(LdapName name, Attributes attributes) throws IOException { + try { + // check consistency + Rdn nameRdn = name.getRdn(name.size() - 1); + Attribute nameAttr = attributes.get(nameRdn.getType()); + if (!nameAttr.get().equals(nameRdn.getValue())) + throw new UserDirectoryException( + "Attribute " + nameAttr.getID() + "=" + nameAttr.get() + " not consistent with DN " + name); + + writer.append(dn.name() + ":").append(name.toString()).append('\n'); + Attribute objectClassAttr = attributes.get("objectClass"); + if (objectClassAttr != null) + writeAttribute(objectClassAttr); + for (NamingEnumeration attrs = attributes.getAll(); attrs.hasMore();) { + Attribute attribute = attrs.next(); + if (attribute.getID().equals(dn.name()) || attribute.getID().equals("objectClass")) + continue;// skip DN attribute + writeAttribute(attribute); + } + writer.append('\n'); + writer.flush(); + } catch (NamingException e) { + throw new UserDirectoryException("Cannot write LDIF", e); + } + } + + public void write(Map entries) throws IOException { + for (LdapName dn : entries.keySet()) + writeEntry(dn, entries.get(dn)); + } + + protected void writeAttribute(Attribute attribute) throws NamingException, IOException { + for (NamingEnumeration attrValues = attribute.getAll(); attrValues.hasMore();) { + Object value = attrValues.next(); + if (value instanceof byte[]) { + String encoded = Base64.getEncoder().encodeToString((byte[]) value); + writer.append(attribute.getID()).append("::").append(encoded).append('\n'); + } else { + writer.append(attribute.getID()).append(':').append(value.toString()).append('\n'); + } + } + } +} diff --git a/org.argeo.ext.jackrabbit/.classpath b/org.argeo.ext.jackrabbit/.classpath new file mode 100644 index 000000000..a8a298a3c --- /dev/null +++ b/org.argeo.ext.jackrabbit/.classpath @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/org.argeo.ext.jackrabbit/.project b/org.argeo.ext.jackrabbit/.project new file mode 100644 index 000000000..fde35ccc8 --- /dev/null +++ b/org.argeo.ext.jackrabbit/.project @@ -0,0 +1,28 @@ + + + org.argeo.ext.jackrabbit + + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.pde.ManifestBuilder + + + + + org.eclipse.pde.SchemaBuilder + + + + + + org.eclipse.pde.PluginNature + org.eclipse.jdt.core.javanature + + diff --git a/org.argeo.ext.jackrabbit/bnd.bnd b/org.argeo.ext.jackrabbit/bnd.bnd new file mode 100644 index 000000000..6bab2aa5e --- /dev/null +++ b/org.argeo.ext.jackrabbit/bnd.bnd @@ -0,0 +1,3 @@ +Fragment-Host: org.apache.jackrabbit.core +Import-Package: org.springframework.core,\ +* diff --git a/org.argeo.ext.jackrabbit/build.properties b/org.argeo.ext.jackrabbit/build.properties new file mode 100644 index 000000000..8cc339269 --- /dev/null +++ b/org.argeo.ext.jackrabbit/build.properties @@ -0,0 +1,23 @@ +source.. = src/,\ + ext/test/ + +additional.bundles = org.junit,\ + org.apache.jackrabbit.core,\ + javax.jcr,\ + org.apache.jackrabbit.api,\ + org.apache.jackrabbit.data,\ + org.apache.jackrabbit.jcr.commons,\ + org.apache.jackrabbit.spi,\ + org.apache.jackrabbit.spi.commons,\ + org.slf4j.api,\ + org.slf4j.commons.logging,\ + org.slf4j.log4j12,\ + org.apache.log4j,\ + org.apache.commons.collections,\ + EDU.oswego.cs.dl.util.concurrent,\ + org.apache.lucene,\ + org.apache.tika.core,\ + org.apache.commons.dbcp,\ + org.apache.commons.pool,\ + org.argeo.jcr + diff --git a/org.argeo.ext.jackrabbit/ext/test/log4j.properties b/org.argeo.ext.jackrabbit/ext/test/log4j.properties new file mode 100644 index 000000000..b4edd7c2e --- /dev/null +++ b/org.argeo.ext.jackrabbit/ext/test/log4j.properties @@ -0,0 +1,17 @@ +log4j.rootLogger=WARN, console + +## Levels +log4j.logger.org.argeo=DEBUG +log4j.logger.org.apache.jackrabbit=OFF +log4j.logger.org.apache.jackrabbit.core.security=DEBUG +log4j.logger.org.apache.jackrabbit.core.DefaultSecurityManager=DEBUG + +## Appenders +# console is set to be a ConsoleAppender. +log4j.appender.console=org.apache.log4j.ConsoleAppender + +# console uses PatternLayout. +log4j.appender.console.layout=org.apache.log4j.PatternLayout +#log4j.appender.console.layout.ConversionPattern= %-5p %d{ISO8601} %m - %c%n +#log4j.appender.console.layout.ConversionPattern=%m%n +log4j.appender.console.layout.ConversionPattern=%d{ABSOLUTE} %m (%F:%L) [%t] %p %n diff --git a/org.argeo.ext.jackrabbit/ext/test/org/argeo/security/jackrabbit/JackrabbitAuthTest.java b/org.argeo.ext.jackrabbit/ext/test/org/argeo/security/jackrabbit/JackrabbitAuthTest.java new file mode 100644 index 000000000..47afff922 --- /dev/null +++ b/org.argeo.ext.jackrabbit/ext/test/org/argeo/security/jackrabbit/JackrabbitAuthTest.java @@ -0,0 +1,52 @@ +package org.argeo.security.jackrabbit; + +import javax.jcr.Repository; +import javax.jcr.Session; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.argeo.jackrabbit.unit.AbstractJackrabbitTestCase; + +public class JackrabbitAuthTest extends AbstractJackrabbitTestCase { + private final Log log = LogFactory.getLog(JackrabbitAuthTest.class); + + public void testLogin() throws Exception { + Session session = session(); + log.debug(session.getUserID()); + assertEquals("admin", session.getUserID()); + // Subject subject = new Subject(); + // LoginContext loginContext = new LoginContext("SYSTEM", subject); + // loginContext.login(); + // Subject.doAs(subject, new PrivilegedExceptionAction() { + // + // @Override + // public Void run() throws Exception { + // Repository repository = getRepository(); + // Session session = repository.login(); + // log.debug(session.getUserID()); + // return null; + // } + // }); + } + + @Override + protected String getLoginContext() { + return LOGIN_CONTEXT_TEST_SYSTEM; + } + + @Override + protected Repository createRepository() throws Exception { + return super.createRepository(); + } + + @Override + protected void clearRepository(Repository repository) throws Exception { + // System.setProperty("java.security.auth.login.config", ""); + } + + @Override + protected String getRepositoryConfigResource() { + return "/org/argeo/security/jackrabbit/repository-memory-test.xml"; + } + +} diff --git a/org.argeo.ext.jackrabbit/ext/test/org/argeo/security/jackrabbit/repository-memory-test.xml b/org.argeo.ext.jackrabbit/ext/test/org/argeo/security/jackrabbit/repository-memory-test.xml new file mode 100644 index 000000000..e28555574 --- /dev/null +++ b/org.argeo.ext.jackrabbit/ext/test/org/argeo/security/jackrabbit/repository-memory-test.xml @@ -0,0 +1,65 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/org.argeo.ext.jackrabbit/pom.xml b/org.argeo.ext.jackrabbit/pom.xml new file mode 100644 index 000000000..15d5e618f --- /dev/null +++ b/org.argeo.ext.jackrabbit/pom.xml @@ -0,0 +1,27 @@ + + + 4.0.0 + + org.argeo.commons + argeo-commons + 2.1.46-SNAPSHOT + .. + + org.argeo.ext.jackrabbit + Extensions Apache Jackrabbit + + + org.argeo.commons + org.argeo.node.api + 2.1.46-SNAPSHOT + + + + + org.argeo.commons + org.argeo.jcr + 2.1.46-SNAPSHOT + test + + + \ No newline at end of file diff --git a/org.argeo.ext.jackrabbit/src/org/argeo/security/jackrabbit/ArgeoAccessControlProvider.java b/org.argeo.ext.jackrabbit/src/org/argeo/security/jackrabbit/ArgeoAccessControlProvider.java new file mode 100644 index 000000000..cd0cf86f2 --- /dev/null +++ b/org.argeo.ext.jackrabbit/src/org/argeo/security/jackrabbit/ArgeoAccessControlProvider.java @@ -0,0 +1,22 @@ +package org.argeo.security.jackrabbit; + +import java.util.Map; + +import javax.jcr.RepositoryException; +import javax.jcr.Session; + +import org.apache.jackrabbit.core.security.authorization.acl.ACLProvider; + +/** Argeo specific access control provider */ +public class ArgeoAccessControlProvider extends ACLProvider { + + @SuppressWarnings({ "rawtypes", "unchecked" }) + @Override + public void init(Session systemSession, Map configuration) + throws RepositoryException { + if (!configuration.containsKey(PARAM_ALLOW_UNKNOWN_PRINCIPALS)) + configuration.put(PARAM_ALLOW_UNKNOWN_PRINCIPALS, "true"); + super.init(systemSession, configuration); + } + +} diff --git a/org.argeo.ext.jackrabbit/src/org/argeo/security/jackrabbit/ArgeoAccessManager.java b/org.argeo.ext.jackrabbit/src/org/argeo/security/jackrabbit/ArgeoAccessManager.java new file mode 100644 index 000000000..52ea3c984 --- /dev/null +++ b/org.argeo.ext.jackrabbit/src/org/argeo/security/jackrabbit/ArgeoAccessManager.java @@ -0,0 +1,50 @@ +/* + * 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.jackrabbit; + +import javax.jcr.PathNotFoundException; +import javax.jcr.RepositoryException; +import javax.jcr.security.Privilege; + +import org.apache.jackrabbit.core.id.ItemId; +import org.apache.jackrabbit.core.security.DefaultAccessManager; +import org.apache.jackrabbit.spi.Path; + +/** + * Intermediary class in order to have a consistent naming in config files. Does + * nothing for the time being, but may in the future. + */ +public class ArgeoAccessManager extends DefaultAccessManager { + + @Override + public boolean canRead(Path itemPath, ItemId itemId) + throws RepositoryException { + return super.canRead(itemPath, itemId); + } + + @Override + public Privilege[] getPrivileges(String absPath) + throws PathNotFoundException, RepositoryException { + return super.getPrivileges(absPath); + } + + @Override + public boolean hasPrivileges(String absPath, Privilege[] privileges) + throws PathNotFoundException, RepositoryException { + return super.hasPrivileges(absPath, privileges); + } + +} diff --git a/org.argeo.ext.jackrabbit/src/org/argeo/security/jackrabbit/ArgeoSecurityManager.java b/org.argeo.ext.jackrabbit/src/org/argeo/security/jackrabbit/ArgeoSecurityManager.java new file mode 100644 index 000000000..978be436b --- /dev/null +++ b/org.argeo.ext.jackrabbit/src/org/argeo/security/jackrabbit/ArgeoSecurityManager.java @@ -0,0 +1,108 @@ +/* + * 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.jackrabbit; + +import java.security.Principal; +import java.util.Set; + +import javax.jcr.RepositoryException; +import javax.jcr.Session; +import javax.security.auth.Subject; +import javax.security.auth.x500.X500Principal; + +import org.apache.jackrabbit.api.security.user.UserManager; +import org.apache.jackrabbit.core.DefaultSecurityManager; +import org.apache.jackrabbit.core.security.AMContext; +import org.apache.jackrabbit.core.security.AccessManager; +import org.apache.jackrabbit.core.security.SecurityConstants; +import org.apache.jackrabbit.core.security.authorization.WorkspaceAccessManager; + +/** Integrates Spring Security and Jackrabbit Security users and roles. */ +public class ArgeoSecurityManager extends DefaultSecurityManager { + @Override + public AccessManager getAccessManager(Session session, AMContext amContext) + throws RepositoryException { + synchronized (getSystemSession()) { + return super.getAccessManager(session, amContext); + } + } + + @Override + public UserManager getUserManager(Session session) + throws RepositoryException { + synchronized (getSystemSession()) { + return super.getUserManager(session); + } + } + + /** + * Since this is called once when the session is created, we take the + * opportunity to make sure that Jackrabbit users and groups reflect Spring + * Security name and authorities. + */ + @Override + public String getUserID(Subject subject, String workspaceName) + throws RepositoryException { + Set userPrincipal = subject + .getPrincipals(X500Principal.class); + if (userPrincipal.isEmpty()) + return super.getUserID(subject, workspaceName); + if (userPrincipal.size() > 1) { + StringBuilder buf = new StringBuilder(); + for (X500Principal principal : userPrincipal) + buf.append(' ').append('\"').append(principal).append('\"'); + throw new RuntimeException("Multiple user principals:" + buf); + } + return userPrincipal.iterator().next().getName(); + // Authentication authentication = SecurityContextHolder.getContext() + // .getAuthentication(); + // if (authentication != null) + // return authentication.getName(); + // else + // return super.getUserID(subject, workspaceName); + } + + @Override + protected WorkspaceAccessManager createDefaultWorkspaceAccessManager() { + WorkspaceAccessManager wam = super + .createDefaultWorkspaceAccessManager(); + return new ArgeoWorkspaceAccessManagerImpl(wam); + } + + private class ArgeoWorkspaceAccessManagerImpl implements SecurityConstants, + WorkspaceAccessManager { + private final WorkspaceAccessManager wam; + + public ArgeoWorkspaceAccessManagerImpl(WorkspaceAccessManager wam) { + super(); + this.wam = wam; + } + + public void init(Session systemSession) throws RepositoryException { + wam.init(systemSession); + } + + public void close() throws RepositoryException { + } + + public boolean grants(Set principals, String workspaceName) + throws RepositoryException { + // TODO: implements finer access to workspaces + return true; + } + } + +} diff --git a/org.argeo.ext.jackrabbit/src/org/argeo/security/jackrabbit/SystemJackrabbitLoginModule.java b/org.argeo.ext.jackrabbit/src/org/argeo/security/jackrabbit/SystemJackrabbitLoginModule.java new file mode 100644 index 000000000..62f8fa02b --- /dev/null +++ b/org.argeo.ext.jackrabbit/src/org/argeo/security/jackrabbit/SystemJackrabbitLoginModule.java @@ -0,0 +1,62 @@ +package org.argeo.security.jackrabbit; + +import java.util.Map; +import java.util.Set; + +import javax.security.auth.Subject; +import javax.security.auth.callback.CallbackHandler; +import javax.security.auth.login.LoginException; +import javax.security.auth.spi.LoginModule; +import javax.security.auth.x500.X500Principal; + +import org.apache.jackrabbit.core.security.SecurityConstants; +import org.apache.jackrabbit.core.security.principal.AdminPrincipal; +import org.argeo.node.DataAdminPrincipal; + +public class SystemJackrabbitLoginModule implements LoginModule { + + private Subject subject; + + @Override + public void initialize(Subject subject, CallbackHandler callbackHandler, Map sharedState, + Map options) { + this.subject = subject; + } + + @Override + public boolean login() throws LoginException { + return true; + } + + @Override + public boolean commit() throws LoginException { + Set initPrincipal = subject.getPrincipals(DataAdminPrincipal.class); + if (!initPrincipal.isEmpty()) { + subject.getPrincipals().add(new AdminPrincipal(SecurityConstants.ADMIN_ID)); + return true; + } + + Set userPrincipal = subject.getPrincipals(X500Principal.class); + if (userPrincipal.isEmpty()) + throw new LoginException("Subject must be pre-authenticated"); + if (userPrincipal.size() > 1) + throw new LoginException("Multiple user principals " + userPrincipal); + + return true; + } + + @Override + public boolean abort() throws LoginException { + return true; + } + + @Override + public boolean logout() throws LoginException { + Set initPrincipal = subject.getPrincipals(DataAdminPrincipal.class); + if (!initPrincipal.isEmpty()) { + subject.getPrincipals(AdminPrincipal.class); + return true; + } + return true; + } +} diff --git a/org.argeo.ext.rap.ui.workbench/.project b/org.argeo.ext.rap.ui.workbench/.project new file mode 100644 index 000000000..751e9d794 --- /dev/null +++ b/org.argeo.ext.rap.ui.workbench/.project @@ -0,0 +1,20 @@ + + + org.argeo.eclipse.ui.workbench.rap + + + + + + org.eclipse.pde.ManifestBuilder + + + + org.eclipse.pde.SchemaBuilder + + + + + org.eclipse.pde.PluginNature + + diff --git a/org.argeo.ext.rap.ui.workbench/META-INF/spring/osgi.xml b/org.argeo.ext.rap.ui.workbench/META-INF/spring/osgi.xml new file mode 100644 index 000000000..206a72ad2 --- /dev/null +++ b/org.argeo.ext.rap.ui.workbench/META-INF/spring/osgi.xml @@ -0,0 +1,19 @@ + + + + + + + + + + \ No newline at end of file diff --git a/org.argeo.ext.rap.ui.workbench/bnd.bnd b/org.argeo.ext.rap.ui.workbench/bnd.bnd new file mode 100644 index 000000000..48c602a93 --- /dev/null +++ b/org.argeo.ext.rap.ui.workbench/bnd.bnd @@ -0,0 +1,4 @@ +Bundle-SymbolicName: org.argeo.ext.rap.ui.workbench;singleton:=true +Bundle-ActivationPolicy: lazy + +Fragment-Host: org.eclipse.rap.ui.workbench diff --git a/org.argeo.ext.rap.ui.workbench/build.properties b/org.argeo.ext.rap.ui.workbench/build.properties new file mode 100644 index 000000000..485b2667a --- /dev/null +++ b/org.argeo.ext.rap.ui.workbench/build.properties @@ -0,0 +1,2 @@ +source.. = src/,\ + ext/test/ diff --git a/org.argeo.ext.rap.ui.workbench/pom.xml b/org.argeo.ext.rap.ui.workbench/pom.xml new file mode 100644 index 000000000..d45618a77 --- /dev/null +++ b/org.argeo.ext.rap.ui.workbench/pom.xml @@ -0,0 +1,11 @@ + + 4.0.0 + + org.argeo.commons + 2.1.46-SNAPSHOT + argeo-commons + .. + + org.argeo.ext.rap.ui.workbench + Extensions Eclipse RAP Workbench + diff --git a/org.argeo.jcr/.classpath b/org.argeo.jcr/.classpath new file mode 100644 index 000000000..a8a298a3c --- /dev/null +++ b/org.argeo.jcr/.classpath @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/org.argeo.jcr/.project b/org.argeo.jcr/.project new file mode 100644 index 000000000..e432547c7 --- /dev/null +++ b/org.argeo.jcr/.project @@ -0,0 +1,28 @@ + + + org.argeo.jcr + + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.pde.ManifestBuilder + + + + + org.eclipse.pde.SchemaBuilder + + + + + + org.eclipse.pde.PluginNature + org.eclipse.jdt.core.javanature + + diff --git a/org.argeo.jcr/bnd.bnd b/org.argeo.jcr/bnd.bnd new file mode 100644 index 000000000..5645d300d --- /dev/null +++ b/org.argeo.jcr/bnd.bnd @@ -0,0 +1,12 @@ +Import-Package: junit.framework;resolution:=optional,\ +org.xml.sax;version="0.0.0",\ +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,\ +org.h2;resolution:=optional,\ +org.postgresql;resolution:=optional,\ +* +Export-Package: org.argeo.jcr.*, org.argeo.jackrabbit.* \ No newline at end of file diff --git a/org.argeo.jcr/build.properties b/org.argeo.jcr/build.properties new file mode 100644 index 000000000..f4baf37e8 --- /dev/null +++ b/org.argeo.jcr/build.properties @@ -0,0 +1,23 @@ +source.. = src/,\ + ext/test/ +output.. = bin/ +bin.includes = META-INF/,\ + . +additional.bundles = org.junit,\ + org.apache.jackrabbit.core,\ + javax.jcr,\ + org.apache.jackrabbit.api,\ + org.apache.jackrabbit.data,\ + org.apache.jackrabbit.jcr.commons,\ + org.apache.jackrabbit.spi,\ + org.apache.jackrabbit.spi.commons,\ + org.slf4j.api,\ + org.slf4j.commons.logging,\ + org.slf4j.log4j12,\ + org.apache.log4j,\ + org.apache.commons.collections,\ + EDU.oswego.cs.dl.util.concurrent,\ + org.apache.lucene,\ + org.apache.tika.core,\ + org.apache.commons.dbcp,\ + org.apache.commons.pool diff --git a/org.argeo.jcr/ext/test/log4j.properties b/org.argeo.jcr/ext/test/log4j.properties new file mode 100644 index 000000000..3d7528963 --- /dev/null +++ b/org.argeo.jcr/ext/test/log4j.properties @@ -0,0 +1,14 @@ +log4j.rootLogger=WARN, console + +## Levels +log4j.logger.org.argeo=DEBUG +log4j.logger.org.apache.jackrabbit=OFF + +## Appenders +# console is set to be a ConsoleAppender. +log4j.appender.console=org.apache.log4j.ConsoleAppender + +# console uses PatternLayout. +log4j.appender.console.layout=org.apache.log4j.PatternLayout +#log4j.appender.console.layout.ConversionPattern= %-5p %d{ISO8601} %m - %c%n +log4j.appender.console.layout.ConversionPattern=%m%n diff --git a/org.argeo.jcr/ext/test/org/argeo/jcr/docbook/DocBookModelTest.java b/org.argeo.jcr/ext/test/org/argeo/jcr/docbook/DocBookModelTest.java new file mode 100644 index 000000000..5af20bad4 --- /dev/null +++ b/org.argeo.jcr/ext/test/org/argeo/jcr/docbook/DocBookModelTest.java @@ -0,0 +1,115 @@ +package org.argeo.jcr.docbook; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.InputStream; +import java.io.InputStreamReader; + +import javax.jcr.RepositoryException; + +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.argeo.jackrabbit.unit.AbstractJackrabbitTestCase; +import org.argeo.jcr.JcrUtils; + +public class DocBookModelTest extends AbstractJackrabbitTestCase { + private final static Log log = LogFactory.getLog(DocBookModelTest.class); + + public void testLoadWikipediaSample() throws Exception { + importXml("WikipediaSample.dbk.xml"); + } + + public void XXXtestLoadHowTo() throws Exception { + importXml("howto.xml", false); + } + + protected void importXml(String res) throws Exception { + importXml(res, true); + } + + protected void importXml(String res, Boolean mini) throws Exception { + byte[] bytes; + try (InputStream in = getClass().getResourceAsStream(res)) { + bytes = IOUtils.toByteArray(in); + } + + {// cnd + long begin = System.currentTimeMillis(); + if (mini) { + InputStreamReader reader = new InputStreamReader(getClass() + .getResourceAsStream( + "/org/argeo/jcr/docbook/docbook.cnd")); + CndImporter.registerNodeTypes(reader, session()); + reader.close(); + } else { + InputStreamReader reader = new InputStreamReader(getClass() + .getResourceAsStream( + "/org/argeo/jcr/docbook/docbook-full.cnd")); + CndImporter.registerNodeTypes(reader, session()); + reader.close(); + } + long duration = System.currentTimeMillis() - begin; + if (log.isDebugEnabled()) + log.debug(" CND loaded in " + duration + " ms"); + } + + String testPath = "/" + res; + // if (mini) + JcrUtils.mkdirs(session(), testPath, "dbk:set"); + // else + // JcrUtils.mkdirs(session(), testPath, "dbk:book"); + + DocBookModel model = new DocBookModel(session()); + try (InputStream in = new ByteArrayInputStream(bytes)) { + long begin = System.currentTimeMillis(); + model.importXml(testPath, in); + long duration = System.currentTimeMillis() - begin; + if (log.isDebugEnabled()) + log.debug("Imported " + res + " " + (bytes.length / 1024l) + + " kB in " + duration + " ms (" + + (bytes.length / duration) + " B/ms)"); + } + + saveSession(); + // JcrUtils.debug(session().getRootNode()); + + try (ByteArrayOutputStream out = new ByteArrayOutputStream()) { + try { + model.exportXml(testPath + "/dbk:book", out); + } catch (Exception e) { + model.exportXml(testPath + "/dbk:article", out); + } + bytes = out.toByteArray(); + + session().logout(); + model.setSession(session()); + + // log.debug(new String(bytes)); + try (InputStream in = new ByteArrayInputStream(bytes)) { + long begin = System.currentTimeMillis(); + model.importXml(testPath, in); + long duration = System.currentTimeMillis() - begin; + if (log.isDebugEnabled()) + log.debug("Re-imported " + res + " " + + (bytes.length / 1024l) + " kB in " + duration + + " ms (" + (bytes.length / duration) + " B/ms)"); + } + } + saveSession(); + } + + protected void saveSession() throws RepositoryException { + long begin = System.currentTimeMillis(); + session().save(); + long duration = System.currentTimeMillis() - begin; + if (log.isDebugEnabled()) + log.debug(" Session save took " + duration + " ms"); + } + + // public static Test suite() { + // return defaultTestSuite(DocBookModelTest.class); + // } + +} diff --git a/org.argeo.jcr/ext/test/org/argeo/jcr/docbook/WikipediaSample.dbk.xml b/org.argeo.jcr/ext/test/org/argeo/jcr/docbook/WikipediaSample.dbk.xml new file mode 100644 index 000000000..29f5b709b --- /dev/null +++ b/org.argeo.jcr/ext/test/org/argeo/jcr/docbook/WikipediaSample.dbk.xml @@ -0,0 +1,13 @@ + + + Very simple book + + Chapter 1 + Hello world! + I hope that your day is proceeding splendidly! + + + Chapter 2 + Hello again, world! + + \ No newline at end of file diff --git a/org.argeo.jcr/ext/test/org/argeo/jcr/docbook/howto.xml b/org.argeo.jcr/ext/test/org/argeo/jcr/docbook/howto.xml new file mode 100644 index 000000000..b8b022aac --- /dev/null +++ b/org.argeo.jcr/ext/test/org/argeo/jcr/docbook/howto.xml @@ -0,0 +1,2295 @@ + + + +YES"> +NO"> +]> + +
+ +DocBook V5.0 +The Transition Guide + + +Jirka Kosek + jirka@kosek.cz +Norman Walsh + ndw@nwalsh.com + §convert4to5, proofreading +Dick Hamilton + rlhamilton@frii.com + §changes-removed, customization, proofreading +Michael(tm) Smith + smith@sideshowbarker.net + §dbxsl-ns + + + +2009-06-16 +2008-02-06 +2007-10-28 +2006-10-22 +2006-05-16 +2006-03-01 +2005-12-28 +2005-10-27 + + + +This document is targeted at DocBook users who are considering +switching from DocBook V4.x to DocBook V5.0. It describes +differences between DocBook V4.x and V5.0 and provides some suggestions about +how to edit and process DocBook V5.0 documents. There is +also a section devoted to conversion of legacy documents from DocBook +4.x to DocBook V5.0. + +At the time this was written the current version of DocBook V5.0 +was &version;. However, almost all of the information in this document is +general and applies to any newer version of DocBook V5.0. + + +
+Introduction + +The differences between DocBook V4.x and V5.0 are quite radical in +some aspects, but the basic idea behind DocBook is still the same, and +almost all element names are unchanged. Because of this it is very +easy to become familiar with DocBook V5.0 if you know any previous version of +DocBook. You can find a complete list of changes in +DB5SPEC, here we will discuss only the most +fundamental changes. + +
+Finally in a namespace + +All DocBook V5.0 elements are in the namespace +http://docbook.org/ns/docbook. XMLExtensible +Markup Language namespaces are used to distinguish +between different element sets. In the last few years, almost all new +XML grammars have used their own namespace. It is easy to +create compound documents that contain elements from different XML +vocabularies. DocBook V5.0 is following this design rule. Using +namespaces in your documents is very easy. Consider this +simple article marked up in DocBook V4.5: + + + Sample article + This is a really short article. +
]]> + +The corresponding DocBook V5.0 article will look very similar: + + + Sample article + This is a really short article. +]]> + +The only change is the addition of a default namespace declaration +(xmlns="http://docbook.org/ns/docbook") on the root +element. This declaration applies the namespace to the root element and +all nested elements. Each +element is now uniquely identified by its local name and namespace. + + +The namespace name http://docbook.org/ns/docbook serves +only as an identifier. This resource is not fetched during processing +of DocBook documents, and you are not required to have an Internet +connection during processing. If you access the namespace URI with a browser, +you will find a short explanatory document about the namespace. In the +future this document will probably conform to (some version of) RDDL +and provide pointers to related resources. + + + + +
+Relaxing with DocBook + +For more than a decade, the DocBook schema was defined using a +DTD. However, DTDs have serious limitations, and DocBook V5.0 is thus +defined using a very powerful schema language called RELAX NG. Thanks +to RELAX NG, it is now much easier to create customized versions of +DocBook, and some content models are now cleaner and more +precise. + +Using RELAX NG has an impact on the document prolog. The following +example shows the typical prolog of a DocBook V4.x document. The version of +the DocBook DTD (in this case 4.5) is indicated in the document type +declaration (!DOCTYPE) which points to a particular version of the +DTD. + + +DocBook V4.5 document + + +
+ Sample article + This is a very short article. +
]]>
+
+ +In contrast, DocBook V5.0 does not depend on DTDs anymore. This +mean that there is no document type declaration and the version of DocBook +used is indicated with the version +attribute instead. + + +DocBook V5.0 document + +
+ Sample article + This is a very short article. +
]]>
+
+ +As you can see, DocBook V5.0 is built on top of existing XML +standards as much as possible, for example the lang attribute is superseded by the standard +xml:lang attribute. + +Another fundamental change is that there is no direct indication +of the schema used. Later in this document, you will learn how you can +specify a schema to be used for document validation. + + +Although we recommend the RELAX NG schema for DocBook +V5.0, there are also DTD and W3C XML Schema versions available (see ) for tools that do not yet support RELAX NG. + + +
+ +
+Why switch to DocBook V5.0? + +The simple answer is because DocBook V5.0 is the +future. Apart from this marketing blurb, there are also more +technical reasons: + + + +DocBook V4.x is feature frozen.DocBook V4.5 +is the last version of DocBook in the V4.x series. Any new DocBook +development, like the addition of new elements, will be done in +DocBook V5.0. It is only matter of time before useful, new elements +will be added into DocBook V5.0, but they are not likely to be back +ported into DocBook V4.x. DocBook V4.x will be in maintenance mode and +errata will be published if necessary. + + +DocBook V5.0 offers new functionality. +DocBook V5.0 provides significant improvements over DocBook V4.x. For +example there is general markup for annotations, a new and flexible +system for linking, and unified markup for information sections using +the info element. + + +DocBook V5.0 is more extensible. Having +DocBook V5.0 in a separate namespace allows you to easily mix DocBook +markup with other XML-based languages like SVG, MathML, XHTML or even +FooBarML. + + +DocBook V5.0 is easier to customize. RELAX +NG offers many powerful constructs that make customization much easier +than it would be using a DTD (see ). + + + +
+ +
+Schema jungle + +Schemas for DocBook V5.0 are available in several formats at + (or the +mirror at ). +Only the RELAX NG schema is normative +and it is preferred over the other schema languages. However, for your +convenience there are also DTD and W3C XML Schema versions provided for DocBook +V5.0. But please note that neither the DTD nor the W3C XML schema are able to +capture all the constraints of DocBook V5.0. This mean that a +document that validates against the DTD or XML schema is not necessarily +valid against the RELAX NG schema and thus may not be a valid +DocBook V5.0 document. See for +summary of constraints that are checked by different schemas. + +DTD and W3C XML Schema versions of the DocBook V5.0 grammar are provided +as a convenience for users who want to use DocBook V5.0 with legacy tools +that don't support RELAX NG. Authors are encouraged to switch to RELAX +NG based tools as soon as possible, or at least to validate documents +against the RELAX NG schema before further processing. + +Some document constraints can't be expressed in schema languages +like RELAX NG or W3C XML Schema. To check for these additional +constraints DocBook V5.0 uses Schematron. We recommend that you +validate your document against both the RELAX NG and +Schematron schemas. + + + Schema Comparison + + + + + + + + + + Description + DTD + W3C XML Schema + W3C XML Schema + Schematron + RELAX NG + RELAX NG + Schematron/NVDL + + + + + Basic document structure + &yes; + &yes; + &yes; + &yes; + &yes; + + + ID/IDREF datatypes + &yes; + &yes; + &yes; + &yes; + &yes; + + + Datatypes + In a very few places RELAX NG specifies datatype + like number (mainly for length specifications) or + enumeration between 0 and + 1. + In general those datatypes can be also supported in + W3C XML Schema, but currently this schema is generated + from DTD which lacks datatype information. + + + &no; + &no; + &no; + &yes; + &yes; + + + Co-occurrences + RELAX NG grammar enforces exclusivity of several + elements. For example if you have title inside + info then it is not allowed to have another + title outside info. Similarly, + models of HTML and CALS tables are separated and validated + properly, where in DTD and WXS only union of both models is + available. + On other places co-occurrences enforces particular + content model based on presence of specific attribute or + attribute value. + Please also note that in theory co-occurences can be + validated using Schematron, but the current DocBook schema + uses RELAX NG for these definitions. Schematron can be used + only for validation, whereas grammar based schemas like + RELAX NG are useful also for other purposes like guided editing. + + &no; + &no; + &no; + &yes; + &yes; + + + Hooks for MathML and SVG content + &no; + &no; + &no; + &yes; + &yes; + + + Link type integrity + Check whether ID/IDREF links are pointing to element + of corresponding type. For example that + footnoteref points to + footnote. + &no; + &no; + &yes; + &no; + &yes; + + + Presence of version + attribute on the root element + &no; + &no; + &yes; + &no; + &yes; + + + Miscellaneous checks + For example consistency of segmented lists, only one + term inside term definition etc. + &no; + &no; + &yes; + &no; + &yes; + + + Element exclusions + Prevents improper nesting of elements, like admonition + inside admonition. + &no; + &no; + &yes; + &no; + &yes; + + + +
+ +
+Where to get the schemas + +The latest versions of schemas can be obtained from . At the time this was +written the latest version was &version;. Individual schemas are +available at the following locations: + + + +RELAX NG schema + + + +RELAX NG schema in compact syntax + + + +DTD + + + +W3C XML Schema + + + +Schematron schema with additional checks + + + + +These schemas are also available from the mirror at +. + +
+ +
+DocBook documentation + +Detailed documentation about each DocBook V5.0 element is +presented in the reference part +of DocBook: The Definitive Guide. + + +Other parts of DocBook: The Definitive +Guide have not yet been updated to reflect the changes +made in DocBook V5.0. Please do not be confused by this. + + +
+ +
+ + + +
+Tool chain + +This section briefly describes tools and procedures to edit and +process content stored in DocBook V5.0. + +
+Editing DocBook V5.0 + +Because DocBook is an XML-based format and XML is a text-based +format, you can use any text editor to create and edit DocBook V5.0 +documents. However, using dumb editors like Notepad is +not very productive. You will do better if you use an editor that +supports XML. Although there are DTD and W3C XML Schemas available for +DocBook V5.0, which means you can use any editor that works with DTDs +or W3C XML Schemas, we recommend that you use the RELAX NG grammar +with DocBook V5.0. The rest of this section contains an overview of +XML editors (listed in alphabetical order) that are known to work with +RELAX NG schemas and that offer guided editing based on the RELAX NG +schema. + +
+Emacs and nXML + +nXML +mode is an add-on for the GNU +Emacs text editor. By installing nXML you can turn Emacs +into a very powerful XML editor that offers guided editing and +validation of XML documents. + +
+Emacs with nXML mode provides guided editing and validation + + + + + + + + +
+ +nXML uses a special configuration file named +schemas.xml to associate schemas with XML +documents. Often you will find this file in the directory +site-lisp/nxml/schema inside the Emacs installation +directory. Adding the following line into the configuration file, +will associate DocBook V5.0 elements with the appropriate +schema: + +<namespace ns="http://docbook.org/ns/docbook" uri="/path/to/docbook.rnc"/> + + +Please note that nXML ships with a file named +docbook.rnc. This file contains the RELAX NG grammar +for DocBook V4.x. Be sure that you associate the DocBook V5.0 namespace +with the corresponding DocBook V5.0 grammar. + + +If you can't edit the global schemas.xml file, +you can create this file in the same directory as your document. nXML will +find associations placed there also. In this case you must create a +complete configuration file like: + +<locatingRules xmlns="http://thaiopensource.com/ns/locating-rules/1.0"> + <namespace ns="http://docbook.org/ns/docbook" uri="/path/to/docbook.rnc"/> +</locatingRules> + +
+ +
+oXygen + +oXygen is a feature +rich XML editor. It has built-in support for many schema languages +including RELAX NG and it is preconfigured with many document types +including DocBook. oXygen will assist you with writing DocBook V5.0 +content, and you will be able to validate your documents against both +RELAX NG and Schematron schemas. + +
+DocBook V5.0 document opened in oXygen + + + + + +
+ +
+DocBook V5.0 document opened in oXygen in Author mode + + + + + +
+ +
+ +
+XML Mind XML editor + +XML +Mind XML editor (XXE) is a visual validating XML editor that +provides a wordprocessor-like interface to users. It is available in +two versions, Standard and Professional. The Standard version is free and +provides everything you need to edit DocBook V5.0 documents. + +
+XML Mind XML Editor – feels almost like MS Word but real DocBook V5.0 markup is created + + + + + +
+ +In order to use DocBook V5.0 in XXE you have to install +an add-on. Go to +OptionsInstall +Add-ons…. Then choose DocBook +5 configuration and press the OK +button. After restart, XXE is ready to work with DocBook V5.0 +documents. + +
+ +
+ +
+Validating DocBook V5.0 + +If you are not using a RELAX NG-based validating editor when you +create documents, we strongly recommend that you validate your +documents against RELAX NG and Schematron schemas before processing +them. Only after successful validation can you be sure that your +document is really DocBook V5.0 and that processing tools will be able +to process it correctly. + +For validation you can use tools that support simultaneous RELAX NG and +Schematron validation, or you can use NVDL to orchestrate validation using +the two schemas. + +
+Using RELAX NG and Schematron + +You can find a list of RELAX NG validators at . It is best to use +validators with support for embedded Schematron rules inside RELAX NG +schemas. Schematron is a rule-based validation language which is used +to impose additional constraints on DocBook documents. Schematron rules +assert conditions which are impossible or difficult to express +in a pure RELAX NG schema. + +Sun +Multi-Schema XML Validator (MSV) is able to validate an XML +document against a RELAX NG schema and Schematron rules at the same time. +To install and use MSV follow these steps: + + + +Download relames.zip from . + + +Unpack the downloaded file into an arbitrary directory. + + +Validate your document using the following command: +java -Xss512K -jar /path/to/relames.jar /path/to/docbook.rng document.xml + +The switch increases the stack size +of the Java virtual machine. This is necessary because the DocBook schema is +quite large. If you get stack overflow errors from MSV, increase +this value. You may get spurious error messages if the value +is too small, so if you get a stack overflow error, ignore any other error +messages and try a larger value for the stack size. +If you are not using Sun's Java implementation, please consult the +documentation for your virtual machine to learn how to increase the stack +size. + + + + +There is also an on-line DocBook V5.0 +validator that validates DocBook V5.0 documents against the normative +RELAX NG schema with embedded Schematron rules. + +
+ +
+Using NVDL + +NVDL is a meta-schema language which can validate a document +against several schemas. DocBook V5.0 comes with a NVDL +schema which specifies that DocBook documents should be validated +against both RELAX NG and Schematron schemas. + +You can find a list of NVDL validators at . The following procedures show how to +install and use the oNVDL and +JNVDL +validators. + + +oNVDL installation and usage + +Download onvdl-20070517.zip. + + +Unpack the downloaded file into an arbitrary directory. + + +Validate your document using the following command: +java -jar /path/to/oNVDL/bin/onvdl.jar /path/to/docbook.nvdl document.xml + + + + +JNVDL installation and usage + +Download the latest release of JNVDL from . + + +Unpack the downloaded file into an arbitrary directory. + + +Modify file jnvdl.bat (or jnvdl.sh on Unix based systems) to include switch directly after java command. + + +On Windows systems, validate your document using the following command: +/path/to/jnvdl/jnvdl -nt -s /path/to/docbook.nvdl document.xml +On Unix systems, validate your document using the following command: +/path/to/jnvdl/jnvdl.sh -nt -s /path/to/docbook.nvdl document.xml + + + +
+ +
+ +
+Processing DocBook V5.0 + +Part of DocBook's great success can be attributed to the +availability of free +tools that can be used to transform DocBook content into various +target formats including HTML and PDF. The DocBook XSL Stylesheets are +very popular tools. + +
+DocBook XSL Stylesheets + +The DocBook stylesheets are designed to process content written in +different versions of DocBook (for example 3.1 and 4.2). Recent +versions of the stylesheets are also able to process DocBook V5.0 +with some limitations. + +You can process DocBook V5.0 documents with the DocBook XSL +stylesheets in exactly the same way you process DocBook V4.x documents. +You do not need special software; you can stick to your preferred +XSLT processor, be it Saxon, xsltproc, Xalan or whatever else (but see +the note about the lost base URI below). + +During document processing, the stylesheets strip +namespaces from DocBook V5.0 to get a document which will be +very similar to DocBook V4.x. This is necessary because from the XSLT +point of view, elements from different namespaces are distinct and cannot +be easily processed by the same set of templates. This process is +completely transparent to the user. If you are processing DocBook V5.0 +documents, the only difference is that you will see the following +additional message: + +Note: namesp. cut : stripped namespace before processing +Note: namesp. cut : processing stripped document + +Although you can successfully use the existing stylesheets to +process DocBook V5.0, there are some limitations and unsupported +features. The unsupported features include: + + +general annotations; +general XLink links on all elements. + + + +During namespace stripping, the base URI of the document is +lost. This means that in rare situations, relatively referenced +resources like images or programlistings can be processed incorrectly. +The stylesheets attempt to compensate for this problem, but that is not always +possible. When an XSLT processor other than Saxon or Xalan is used, a warning +message is generated: + +WARNING: cannot add @xml:base to node set root element. Relative paths may not work. + + + +
+ +
+DocBook XSL-NS Stylesheets +As you can see from reading the previous section, namespace + stripping has limitations that will cause trouble in some + situations. To overcome those limitations, Bob Stayton created a + build system for taking the non-namespace-aware DocBook XSL + stylesheets and generating namespace-aware versions from them. + The DocBook XSL-NS stylesheets are the result. + +The DocBook XSL-NS stylesheets are released side-by-side + with the DocBook XSL stylesheets, as a separate docbook-xsl-ns package. They are the +recommended XSLT 1.0 stylesheets to use for transforming +namespaced (DocBook V5.0) documents. +
+ +
+XSLT 2.0 based re-implementation + +XSLT 1.0 is missing some important features. To work around +these missing features, the current DocBook XSL stylesheets use some +implementation-specific extensions. +XSLT 2.0 adds many new and previously missing features into the language. +A new set of DocBook stylesheets is being implemented based on XSLT 2.0 +to take advantage of these features and to fully support DocBook V5.0. + + +The XSLT 2.0 based stylesheets have many new features, including: + + +seamless integration of profiling (conditional +documents) with external bibliographies and +glossaries; +no need for (most) external extensions; +internationalized indexes; +easy to customize titlepage templates. + + +The XSLT 2.0 based stylesheets are still under development. At +this writing, they only support HTML and chunked HTML output. As time +permits, the stylesheet developers will be adding other formats. Since +the stylesheets are developed in the limited free time the developers +have, there's no specific schedule. + +There are not very many XSLT 2.0 implementations available. +But, if you want to try the new stylesheets, grab a snapshot of +the development version from +and unpack it somewhere. Then download and install Saxon 9 from . + +To transform a DocBook V5.0 document to a single HTML page use the command: + +java -jar /path/to/saxon9.jar -o output.html document.xml /path/to/docbook-xsl2-snapshot/html/docbook.xsl + +To transform a DocBook V5.0 document to a set of chunked HTML pages use the command: + +java -jar /path/to/saxon9.jar document.xml /path/to/docbook-xsl2-snapshot/html/chunk.xsl + +
+ +
+ +
+ +
+Markup changes + +This section describes the most common markup changes +between DocBook V4.x and V5.0. +You can find a complete list of changes in +DB5SPEC. + +
+Improved cross-referencing and linking + +In DocBook V4.x the attribute id is +used to assign a unique identifier to an element. In DocBook V5.0 this +attribute is renamed xml:id in order +to comply with XMLID. + +Now you can use almost any inline element as the source of a link, +not just xref or link. For example, the following +DocBook 4.x content: + + + DIR command + ... +
+ +
+ LS command + This command is a synonym for DIR command. +
]]> + +is written in DocBook V5.0 as: + + + DIR command + ... +
+ +
+ LS command + This command is a synonym for DIR command. +
]]> + +The linkend attribute was added to all +inline elements together with the href +attribute from the XLink namespace. This means that you can use any inline +element as the source of a hypertext link. To use XLinks you have +to declare the XLink namespace (most often on the root element of your +document): + + + Test article + + Emacs + is my favourite text editor.]]> + … + +The ulink element was removed from DocBook V5.0 +in favor of XLink linking. Instead of the DocBook V4.x ulink +element: + +DocBook site]]> + +you can now use link + +DocBook site]]> + +XLink links may contain a fragment identifier, which you can +use instead of linkend to form +cross-references inside a document; for example: + +DIR]]> + +However XLink links are not checked during validation, while xml:id/linkend +links are checked for ID/IDREF consistency. +One place where the XLink-based, fragment identifier scheme is +useful is when XInclude is being used, since XML ID/IDREF links +cannot span XInclude boundaries. +You can use whichever approach better suits your needs. + + +
+Renamed elements + +Some elements were renamed to better express their meaning or to +reduce the total number of elements available in DocBook. + + +Renamed elements + + + +Old name +New name + + + + +sgmltag +tag + + +bookinfo, articleinfo, +chapterinfo, *info +info + + +authorblurb +personblurb + + +collabname, corpauthor, +corpcredit, corpname +orgname + + +isbn, issn, +pubsnumber +biblioid + + +lot, lotentry, tocback, +tocchap, tocfront, toclevel1, +toclevel2, toclevel3, toclevel4, +toclevel5, tocpart +tocdiv + + +graphic, graphicco, +inlinegraphic, mediaobjectco +mediaobject and inlinemediaobject + + +ulink +link + + +ackno +acknowledgements + + + +
+ +
+ +
+Removed elements + +The following elements were removed from DocBook V5.0 without +direct replacements: action, beginpage, highlights, +interface, invpartnumber, medialabel, modespec, +structfield, structname. +If you use one or more of these elements, here are some suggestions +as to how to re-code them in DocBook V5.0. + + + +Recommended mapping for removed elements + + + +Old name +Recommended mapping + + + + +action +Use <phrase remap="action">. + + +beginpage +Remove: beginpage is advisory only +and has tended to cause confusion. A processing instruction or +comment should be a workable replacement if one is needed. + + +highlights +Use abstract. Note that because highlights has a broader content model, you may +need to wrap contents in a para inside +abstract. + + +interface +Use one of the gui* elements +(guibutton, guiicon, guilabel, +guimenu, guimenuitem, or +guisubmenu). + + +invpartnumber +Use <biblioid class="other" +otherclass="medialabel">. The +productnumber element is another alternative. + + +medialabel +Use <citetitle +pubwork="mediatype">, +where mediatype is the type of media being +labeled (e.g.,cdrom or dvd). + + +modespec +No longer needed. The current processing model for +olink renders modespec +unnecessary. + + +structfield, structname +Use varname. If you need to distinguish between the +two, use <varname +remap="structname or +structfield">. In some contexts, it +may also be appropriate to use property for structfield. + + + +
+ +
+ + + +
+Converting DocBook V4.x documents to DocBook V5.0 + +The DocBook V5.0 schema ships with an XSLT 1.0 stylesheet that +is designed to transform valid DocBook V4.x documents to valid +DocBook V5.0 documents. + +To convert your document, doc.xml in the +examples below, follow these steps: + + + +Check the validity of your DocBook XML V4.x document. The +conversion tool assumes that the input document is valid. If the input +document contains markup errors, the results will be unpredictable at +best. + + +Transform doc.xml to +newdoc.xml with the +db4-upgrade.xsl stylesheet included in the +DocBook V5.0 distribution that you are using. + + +Check the validity of your DocBook XML V5.0 document against +the DocBook V5.0 RELAX NG grammar. + + + +In the vast majority of cases, the resulting document should +be valid and your conversion process is finished. + +If the document is not valid, please report the problem. +(Over time, we'll have more experience with the sorts of things +that can go wrong and we'll update this document to reflect that +experience.) + +
+What About Entities? + +Using XSLT to transform existing documents to DocBook V5.0 has +one potential disadvantage: it removes all entity references from +your document. + +If preserving entities is an important aspect of your production +work flow, you will have to engage in a semi-manual process to +preserve them. + + + +Open your existing document using your favorite editing tool. +You must use a tool that is not XML-aware, or one +that allows you to edit markup “in the raw”. + + +Replace all occurrences of the entity references that you want +to preserve with some unique string. For example, if you want to preserve +“&Product;” references, you could replace them +all with “[[[Product]]]” (assuming that the string +“[[[Product]]]” doesn't occur anywhere else in your document). + + +Copy the document type declaration off of your document and save +it some place. The document type declaration is everything from +“<!DOCTYPE” to the closing “]>”. + + + +Perform the conversion described in . + + + +Open the new document using your favorite editing tool. Replace +all occurrences of the unique string you used to save the entity references +with the corresponding entity references. + + +Paste the document type declaration that you saved onto the top +of your new document. + + +Remove the external identifier (the PUBLIC +and/or SYSTEM keywords) from the document type +declaration. A document that begins: + +]>]]> +is perfectly well-formed. If you don't remove the references to +the DTD, then your parser will likely try to validate against DocBook +V4.0 and that's not going to work. Alternatively, you could refer +to the DocBook V5.0 DTD. + + + + +Steps 2 and 5 from previous procedure can be automated using the +cloak +script written by Michael Smith. + + +
+External Parsed Entities + +External parsed entities, entities which load part of a document +from another file, are a special case. These can often be replaced +with XInclude elements. + +The Perl script db4-entities.pl, also included +in the DocBook V5.0 distribution attempts to perform this replacement +for you. To use the script, perform the following steps: + + + +Process your document with db4-entities.pl. +The script expects a single filename and prints the XInclude version +on standard output. + + +Process the XInclude version as described in . + + + +
+
+ +
+ +
+ Customizing DocBook V5.0 + + + + It's much easier to customize DocBook V5.0 than it was to + customize earlier releases. This is partly because RELAX NG + provides better support for modifications than DTDs and partly + because the DocBook schema is designed to take full advantage + of the capabilities RELAX NG provides. + This section describes the organization of the RELAX NG schema for + DocBook, methods and examples for adding, removing, and modifying elements + and attributes, and conventions for naming and versioning + DocBook customizations. + It assumes some familiarity with RELAX NG. If you are unfamiliar + with RELAX NG, you can find a tutorial introduction in + RNCTUT. + +
+ DocBook RELAX NG schema organization + + The DocBook RELAX NG schema is highly modular, using named + patterns extensively. Every element, attribute, attribute + list, and enumeration has its own named pattern. In addition, + there are named patterns for logical combinations of elements + and attributes. These named patterns provide hooks + into the schema that allow you to do a wide range of customization + by simply redefining one or more of the named patterns. + + + An important design characteristic of the schema is that + duplication is minimized. This is done through the use of + named patterns for common groupings that can be re-used. + For example, the imagedata and videodata + elements each have an align attribute + that takes the same set of enumerated values. Rather than + repeating those values, a single pattern, + db.halign.enumeration is referenced by + the db.videodata.align.enumeration + and db.imagedata.align.enumeration patterns, + which are in turn referenced by the + db.videodata.align.attribute + and db.imagedata.align.attribute patterns. + While this may seem like overkill, it allows a customizer to modify + the allowed enumerations for these two attributes separately or together, + or to completely re-define the allowed content of either or both, + by redefining one or more of these named patterns. + +
Pattern Names + + Because named patterns are used extensively, the RELAX NG schema uses + several naming conventions. These are: + + + + Names have two or more parts, separated by dots . + + + + + The first part of each name is the prefix db + + + + + Each element has a named pattern in the form + db.elementname. + Elements that have different content models in different + contexts will also have patterns in the form + db.context.elementname. For example, db.figure.info + defines the content model for the info element + when it appears as a child of the figure element. + Context may have several parts. + For example, db.cals.entrytbl.thead. + + + + + Most attributes have a named pattern in the form + db.attributename.attribute. + Attributes that have different content models in different + contexts will also have patterns in the form + db.context.attributename.attribute. + For example, + db.olink.localinfo.attribute defines the content + model of the localinfo attribute when + it appears in olink. + There are a few attributes that do not have individual named + patterns. For example, the effectivity attributes are grouped + into db.effectivity.attributes and not identified + separately. + + + + + Each element has a named pattern for its attribute list in + the form + db.elementname.attlist + + that defines the list of attributes for that element. + Elements that have different attribute lists in different + contexts will also have patterns in the form + db.context.elementname.attlist + For example, db.html.table.attlist defines + the attribute list for the html table element and + db.cals.table.attlist defines the attribute + list for a cals table element. + + + + + Each attribute that has enumerated values has a + named pattern in the form + db.[context.]attributename.enumeration. + If the enumeration for a particular attribute depends on + context, optional context is provided. + For example, + db.verbatim.continuation.enumeration defines + the enumeration values for the + continuation attribute that is used + in verbatim contexts like screen. + Unlike elements and attributes, there is not necessarily a + named pattern for enumerated attributes outside their context. + For example, there is no db.class.enumeration + because the class attribute has + a broad and non-intersecting range of uses. + + + + + There are several different groupings of elements and attributes. + Here are the major ones: + + + inlines + + + Combinations of inline elements, for example, + db.error.inlines, which contains + db.errorcode, + db.errortext, etc. + + + + + blocks + + + Combinations of block elements, for example, + db.verbatim.blocks, which contains + db.programlisting, + db.screen, etc. + + + + + attributes + + + Combinations of attributes, for example, + db.effectivity.attributes, + which contains the attributes + arch, + condition, + conformance, etc. + + + + + components + + + High level components of the schema, for example, + db.navigation.components, which contains + db.glossary, + db.bibliography, + db.index, and + db.toc, and is used inside the + content model for chapter, appendix, + and preface. + + + + + contentmodel + + + Shared content models, for example, + db.admonition.contentmodel, which contains + the content model for tip, warning, + note, etc. + + + + + + + There are a couple of other groupings designed to minimize + duplication, but these are the most important. + + + + +
+
+
+ General customization considerations + + Creating a customized schema is similar to + creating a customization layer for XSL. The schema customization + layer is a new RELAX NG schema that defines your changes and + includes the standard docbook schema. You then validate using + the schema customization as your schema. + + + is an empty + RELAX NG customization that does nothing + except define the name spaces and include the standard DocBook schema. + The href attribute of the + include element points to + the location of the standard DocBook V5.0 + schema.The examples in this section use + docbook.rng as the schema location. If you want + to create a portable schema customization you should use a standard + web-accessible location like + http://docbook.org/xml/&version;/rng/docbook.rng and + then use XML + catalogs to resolve this location to your local copy of the + schema for improved performance. Unfortunately, at the time of + this writing not all RELAX NG validators support XML catalogs. + All of the examples are given in both RNG and RNC form. +Empty customization file + + + + + + +]]> + + + +
+
+ Elements +
+ Adding elements + + Adding an element typically takes two definitions. + The first defines the new element and + its content model, and the second adds the + new element into the schema. We'll show two examples. + + + + adds a new element, + person, with the same + content model as author. The new element will be + allowed to appear wherever author can appear. + + + The db.author pattern is copied + and renamed dbx.person, defining + a new element called person. + Then, the db.author pattern is redefined + to be a choice of the current value or dbx.person. + The combine attribute tells + RELAX NG to combine this pattern with the existing named + pattern. In this case, the value + of the combine attribute is + choice, which tells the parser that either + the original pattern or this new pattern is a valid match. + +Adding a new element by duplicating an existing one + + + + + + + + + + + + + + +]]> + + + + The preceding method works well when you'd like a new element + to be a clone or near-clone of an existing element. It gives + you complete control over the content model, but + only limited control over where the element is allowed. It + works well when you want to allow the element in the same places + as an existing element, and for this example that works + nicely, since author is allowed in four different + named patterns, each of which would have had to be redefined to + allow person. + But, if you can't find an existing element that is allowed in + exactly the places you need, this method doesn't work as well. + + + + adds two new elements by combining them into + a higher level pattern. In this example, we'll add + two new inline elements for writing about assembly language, + register and + instruction. + We will allow them wherever programming inlines + or operating system inlines are allowed. + + defines the two elements, creates a new named pattern + (dbx.asm.inlines) that contains them, and adds + that pattern to db.programming.inlines and + db.os.inlines. Since these two patterns + don't have any elements in common, the strategy used in + + would require selecting two different elements to clone, + which would be messy. + +Adding new inline elements + + + + + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + +
+
+ Deleting elements + + Deleting elements is straightforward, but takes some + care and planning. deletes + the important admonition element by redefining + it with a content model of notAllowed. + Note that in this example, the redefinition is inside + the include element. + This is required for + redefinitions that completely replace an existing pattern. + + + Be careful; If you delete an element that is a required part + of another element's content model, you can make it + impossible to create a valid document. + For example, if you delete the title + element, you won't be able to validate a book + because a book requires a title. + +Deleting an element + + + + + + + + +]]> + + +
+
+ Customizing the content model of existing elements + + + expands the definition of author to include two + new elements, born and + died. + The author element allows two content models, + db.person.author.contentmodel, which + defines an author who is a person, and + db.org.author.contentmodel, which + defines an author that is an organization. We will modify + db.person.author.contentmodel so that + only authors who are persons can have the new elements. +Modifying the content model of an element + + + + + + + + + + + + + + + + + + +]]> + + + + + This modification will allow instances like this: + + Babe Ruth + 02/06/1895 + 08/16/1948 +]]> +but because we only modified the content model for authors +who are human, it won't allow an instance like this, which +uses db.org.author.contentmodel: + + + Boston Red Sox + 1919 + 2004 +]]> + +
+
+
+ Attributes +
+ Adding attributes + + The simplest way to add an attribute to a single element + is to add it to the attlist pattern for that element. + + adds the optional attributes born + and died to the attribute + list for author. + The db.author.attlist + named pattern is redefined with the + combine attribute set to + interleave, which interleaves the two new + optional attributes with the existing attributes on the list. + +Adding attributes + + + + + + + + + + + + + + + + + + +]]> + + + + Unlike + , + allows + the new attributes to appear on any author + element, not just those using the person content model. + + + shows + how you could limit the use of these attributes to authors who + are persons. In this example, the new attributes are interleaved + with the db.person.author.contentmodel. + The only difference between this example and + is + that the added patterns are identified as attributes rather than + elements. This shows some of the flexibility of RELAX NG, which + treats attributes and elements very consistently. +Adding attributes; alternate method + + + + + + + + + + + + + + + + + + +]]> + + +There is one difference in the treatment of attributes and elements +that is worth noting. By the XML 1.0 definition, the relative order +of attributes is not significant. Therefore, the +interleave block is not required for +attributes, though it does no harm. + +
+
+ Deleting attributes + + Deleting an attribute is similar to deleting an element, + except that you use the RELAX NG empty + pattern rather than notAllowed. + + deletes the linking attributes, which are collected in the + db.common.linking.attributes pattern, + by defining that pattern as empty. + +Deleting an attribute + + + + + + + +]]> + + + + Generally, empty is used when deleting + attributes and notAllowed is used when + deleting elements. + +
+
+ Changing permitted content of attributes + + + modifies db.spacing.enumeration to + add the additional value large. Note + that to remove a value from an enumeration, you need + to redefine the entire enumeration, minus the values + you don't need. + +Deleting an attribute + + + + + + large + +]]> + + +
+
+ +
+Naming and versioning DocBook customizations + +DocBook V5.0 is not tightly coupled with some particular +validation technology like DTDs. This also means that DocBook V5.0 +documents don't have to (and usually don't) start with a +document type declaration (<!DOCTYPE…>) to specify the schema +(DTD) to use. Instead, DocBook V5.0 instances can be easily +distinguished from other XML vocabularies by using elements in the +http://docbook.org/ns/docbook namespace. This namespace is +enough to distinguish DocBook from other XML based formats. But the +DocBook schema evolves over time and there are several versions of +DocBook (e.g. 3.1, 4.2, 4.5 and 5.0). Since DocBook version 5.0, the +actual version used is indicated in the version attribute on a root element. + + + … +]]> + +Future versions of DocBook documents will start with the same +markup, except the version number will be raised, for example to 5.1 +or 6.0. +The namespace will remain the same until the semantics of the elements +change in a backward incompatible way, which is very unlikely to happen. + +If you create a DocBook schema customization you must change the version attribute to distinguish your +customization from the official DocBook. Changing the +namespace is not recommended because that would break the processing +tools. Remember that changing namespaces is the same as renaming all +elements in the namespace. + +When you customize the schema, use the following syntax to +identify your DocBook derivation: + +base_version-[subset|extension|variant] [name[-version]?]+ + +For example: + +5.0-subset simplified-1.0 +5.0-variant ASMBook +5.0-variant ASMBook-2006 +5.0-extension MathML-2.0 SVG-1.1 + +The first part of the version identifier is the version number of the +DocBook schema from which you derived your customization. + +If your schema is a proper subset, you can advertise this status +by using the subset keyword in the description. If +your schema contains any markup model extensions, you can advertise +this status by using the extension keyword. If +you'd rather not characterize your variant specifically as a subset or +an extension, use the variant keyword. + +After these keywords you may add a whitespace separated list of +customization identifiers. Each name may be optionally followed by its +version number. + +
+ +
+ +
+FAQ + + + +Authoring + + + +How do I attach a schema to a DocBook V5.0 document when I do not +want to use DTDs and !DOCTYPE? + + +There is no standard way of associating a RELAX NG schema with a +document. Most tools provide some mechanism for performing this +association, consult the documentation for your application. In some +tools you must specify schema manually each time you want to +edit/process your document. + + + + + +How do I use entities like ndash in +DocBook V5.0? + + +Modern schema languages (including RELAX NG and W3X XML Schema) +do not provide any means to define entities that can be used for easier +typing of special characters. Some editors provide functions or +special toolbars that allow you to easily pick necessary character +and insert it into document as a raw Unicode character or a numeric +character reference. +Another possibility is to include entity definitions in the +prolog of your document. Entity definition +files are now maintained by W3C. You can reference definition +files with entity definitions you are interested in and then reference +imported entities. For example: + + +%isopub; +]> +
+DocBook V5.0 – the superb documentation format]]> +… +For your convenience there is also flattened entity definition +file which contains all entity definitions. + + +%allent; +]> +
+DocBook V5.0 – the superb documentation format]]> +… + + + + + +How to modularize documents? + + +You can use XInclude for this +task. There is an alternative schema for DocBook V5.0 that +contains XInclude elements. This is necessary to make some XML editors +happy. This schema can be found in files that end with letters xi, e.g. +docbookxi.rnc instead of +docbook.rnc. + + + + + +How to validate documents which are composed by XInclude? + + +If you are using XIncludes you should make sure that the final +document after resolving all inclusions is valid DocBook V5.0 +instance. This means that all XIncludes should be processed before +validation takes place. The following command can be used to enable +XInclude processing in oNVDL. +java -Dorg.apache.xerces.xni.parser.XMLParserConfiguration=org.apache.xerces.parsers.XIncludeParserConfiguration -jar /path/to/oNVDL/bin/onvdl.jar /path/to/docbook.nvdl document.xml +For JNVDL you can use switch to enable XInclude processing. + + + + + + +Stylesheets + + + +Will the current DocBook XSL stylesheets (XSLT 1.0 based +implementation) be maintained and improved in the future since work on +a new XSLT 2.0 based implementation has started? + + +Yes, the current stylesheets (like 1.73.x) will be supported and +improved further because they are very widely deployed and work with +many existing XSLT processors. +Surely there will be a point in a future when all new development +will be switched to the XSLT 2.0 based implementation. But this +will not happen until all features of the current stylesheets are +implemented in the new stylesheets, and until there is more than +one usable XSLT 2.0 processor available. + + + + + + +Schema customizations + + + +How can I extend the DocBook schema with MathML elements? + + +The basic DocBook schema allows elements from the MathML namespace +to appear inside the equation element. This means that you can +validate a DocBook+MathML document, but MathML content will be ignored +during the validation. You will also not be able to use guided editing +for the MathML content. +If you need strict validation of MathML content or guided +editing for MathML, you can easily extend the base DocBook schema with +the MathML schema. + +Extending the DocBook schema with the MathML schema + +Download the MathML RELAX NG schema from and unpack it +somewhere (e.g. into a mathml subdirectory). + + +Create a schema customization in compact syntax—dbmathml.rnc: +namespace html = "http://www.w3.org/1999/xhtml" +namespace mml = "http://www.w3.org/1998/Math/MathML" +namespace db = "http://docbook.org/ns/docbook" + +include "/path/to/docbook.rnc" { + db._any.mml = external "mathml/mathml2.rnc" + db._any = + element * - (db:* | html:* | mml:*) { + (attribute * { text } + | text + | db._any)* + } +} +Or, alternatively, you can use the XML syntax of RELAX NG—dbmathml.rng: + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + +Now use the customized schema (dbmathml.rnc +or dbmathml.rng) instead of the original +DocBook schema. + + + + + + + +How can I extend the DocBook schema with SVG elements? + + +The situation is the same as with MathML support. You can use +elements from the SVG namespace inside the imageobject +element. + +Extending the DocBook schema with the SVG schema + +Download the SVG RELAX NG schema from and unpack it +somewhere (e.g. into an svg subdirectory). + + +Create a schema customization in compact syntax—dbsvg.rnc: +namespace html = "http://www.w3.org/1999/xhtml" +namespace db = "http://docbook.org/ns/docbook" +namespace svg = "http://www.w3.org/2000/svg" + +include "/path/to/docbook.rnc" { + db._any.svg = external "svg/svg11.rnc" + db._any = + element * - (db:* | html:* | svg:*) { + (attribute * { text } + | text + | db._any)* + } +} +Or, alternatively, you can use the XML syntax of RELAX NG—dbsvg.rng: + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + +Now use the customized schema (dbsvg.rnc +or dbsvg.rng) instead of the original +DocBook schema. + + + + + + + +Is it possible to use the previous two customizations for MathML +and SVG together? + + +Yes, you can create a special schema customization that combines +both MathML and SVG with the DocBook schema. In compact syntax, the merged +schema is: +namespace html = "http://www.w3.org/1999/xhtml" +namespace mml = "http://www.w3.org/1998/Math/MathML" +namespace db = "http://docbook.org/ns/docbook" +namespace svg = "http://www.w3.org/2000/svg" + +include "/path/to/docbook.rnc" { + db._any.mml = external "mahtml/mathml2.rnc" + db._any.svg = external "svg/svg11.rnc" + db._any = + element * - (db:* | html:* | mml:* | svg:*) { + (attribute * { text } + | text + | db._any)* + } +} +Or alternatively in the full RELAX NG syntax: + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + + + + +Are there any other examples of schema customization +available? + + +Sure. Some of the are listed bellow: + +Sample +customization of ITS and DocBook +Examples on +DocBook WiKi + + + + + + + +Tool specific problems + + + +I'm using Altova XMLSpy to validate DocBook V5.0 instances +against the W3C XML Schema (docbook.xsd). XMLSpy +complains about undefined xml:id +attributes? + + +XMLSpy always uses its own bundled version of +xml.xsd which unfortunately doesn't define the xml:id attribute. The bundled version of +xml.xsd is hardwired into the program and cannot +be replaced by a newer version. To solve this problem you must upgrade +to version 2006 SP1. + + + + + + +
+ + + + +RNCTUT +Clark, James – Cowan, John – MURATA, Makoto: RELAX NG Compact Syntax Tutorial. +Working Draft, 26 March 2003. OASIS. + + + +NVDLTUT +Nálevka, Petr: +NVDL Tutorial. + + + + +XMLID +Marsh, Jonathan – +Veillard, Daniel – +Walsh, Norman: xml:id Version 1.0. W3C Recommendation, 9 September 2005. + + + +DB5SPEC +Norman, Walsh: The DocBook Schema. +Working Draft 5.0a1, OASIS, 29 June 2005. + + + + + +
diff --git a/org.argeo.jcr/ext/test/org/argeo/jcr/tabular/JcrTabularTest.java b/org.argeo.jcr/ext/test/org/argeo/jcr/tabular/JcrTabularTest.java new file mode 100644 index 000000000..8896b2f4b --- /dev/null +++ b/org.argeo.jcr/ext/test/org/argeo/jcr/tabular/JcrTabularTest.java @@ -0,0 +1,85 @@ +/* + * 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.jcr.tabular; + +import java.io.InputStreamReader; +import java.util.ArrayList; +import java.util.List; + +import javax.jcr.Node; +import javax.jcr.PropertyType; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.jackrabbit.commons.cnd.CndImporter; +import org.argeo.jackrabbit.unit.AbstractJackrabbitTestCase; +import org.argeo.jcr.ArgeoNames; +import org.argeo.jcr.ArgeoTypes; +import org.argeo.util.tabular.TabularColumn; +import org.argeo.util.tabular.TabularRow; +import org.argeo.util.tabular.TabularRowIterator; +import org.argeo.util.tabular.TabularWriter; + +public class JcrTabularTest extends AbstractJackrabbitTestCase { + private final static Log log = LogFactory.getLog(JcrTabularTest.class); + + public void testWriteReadCsv() throws Exception { + session().setNamespacePrefix("argeo", ArgeoNames.ARGEO_NAMESPACE); + InputStreamReader reader = new InputStreamReader(getClass() + .getResourceAsStream("/org/argeo/jcr/argeo.cnd")); + CndImporter.registerNodeTypes(reader, session()); + reader.close(); + + // write + Integer columnCount = 15; + Long rowCount = 1000l; + String stringValue = "test, \ntest"; + + List header = new ArrayList(); + for (int i = 0; i < columnCount; i++) { + header.add(new TabularColumn("col" + i, PropertyType.STRING)); + } + Node tableNode = session().getRootNode().addNode("table", + ArgeoTypes.ARGEO_TABLE); + TabularWriter writer = new JcrTabularWriter(tableNode, header, + ArgeoTypes.ARGEO_CSV); + for (int i = 0; i < rowCount; i++) { + List objs = new ArrayList(); + for (int j = 0; j < columnCount; j++) { + objs.add(stringValue); + } + writer.appendRow(objs.toArray()); + } + writer.close(); + session().save(); + + if (log.isDebugEnabled()) + log.debug("Wrote tabular content " + rowCount + " rows, " + + columnCount + " columns"); + // read + TabularRowIterator rowIt = new JcrTabularRowIterator(tableNode); + Long count = 0l; + while (rowIt.hasNext()) { + TabularRow tr = rowIt.next(); + assertEquals(header.size(), tr.size()); + count++; + } + assertEquals(rowCount, count); + if (log.isDebugEnabled()) + log.debug("Read tabular content " + rowCount + " rows, " + + columnCount + " columns"); + } +} diff --git a/org.argeo.jcr/ext/test/org/argeo/server/jcr/JcrResourceAdapterTest.java b/org.argeo.jcr/ext/test/org/argeo/server/jcr/JcrResourceAdapterTest.java new file mode 100644 index 000000000..3ce499af8 --- /dev/null +++ b/org.argeo.jcr/ext/test/org/argeo/server/jcr/JcrResourceAdapterTest.java @@ -0,0 +1,101 @@ +/* + * 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.jcr; + +import java.io.InputStream; +import java.text.SimpleDateFormat; +import java.util.Calendar; +import java.util.List; + +import org.apache.commons.io.IOUtils; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.argeo.jackrabbit.unit.AbstractJackrabbitTestCase; +import org.argeo.jcr.JcrResourceAdapter; +import org.springframework.core.io.ClassPathResource; +import org.springframework.core.io.Resource; + +public class JcrResourceAdapterTest extends AbstractJackrabbitTestCase { + private static SimpleDateFormat sdf = new SimpleDateFormat( + "yyyyMMdd:hhmmss.SSS"); + + private final static Log log = LogFactory + .getLog(JcrResourceAdapterTest.class); + + private JcrResourceAdapter jra; + + public void testCreate() throws Exception { + String basePath = "/test/subdir"; + jra.mkdirs(basePath); + Resource res = new ClassPathResource("org/argeo/server/jcr/dummy00.xls"); + String filePath = basePath + "/dummy.xml"; + jra.create(filePath, res.getInputStream(), "application/vnd.ms-excel"); + InputStream in = jra.retrieve(filePath); + assertTrue(IOUtils.contentEquals(res.getInputStream(), in)); + } + + public void testVersioning() throws Exception { + String basePath = "/test/versions"; + jra.mkdirs(basePath); + String filePath = basePath + "/dummy.xml"; + Resource res00 = new ClassPathResource( + "org/argeo/server/jcr/dummy00.xls"); + jra.create(filePath, res00.getInputStream(), "application/vnd.ms-excel"); + Resource res01 = new ClassPathResource( + "org/argeo/server/jcr/dummy01.xls"); + jra.update(filePath, res01.getInputStream()); + Resource res02 = new ClassPathResource( + "org/argeo/server/jcr/dummy02.xls"); + jra.update(filePath, res02.getInputStream()); + + List versions = jra.listVersions(filePath); + log.debug("Versions of " + filePath); + int count = 0; + for (Calendar version : versions) { + log.debug(" " + (count == 0 ? "base" : count - 1) + "\t" + + sdf.format(version.getTime())); + count++; + } + + assertEquals(4, versions.size()); + + InputStream in = jra.retrieve(filePath, 1); + assertTrue(IOUtils.contentEquals(res01.getInputStream(), in)); + in = jra.retrieve(filePath, 0); + assertTrue(IOUtils.contentEquals(res00.getInputStream(), in)); + in = jra.retrieve(filePath, 2); + assertTrue(IOUtils.contentEquals(res02.getInputStream(), in)); + Resource res03 = new ClassPathResource( + "org/argeo/server/jcr/dummy03.xls"); + jra.update(filePath, res03.getInputStream()); + in = jra.retrieve(filePath, 1); + assertTrue(IOUtils.contentEquals(res01.getInputStream(), in)); + } + + @Override + protected void setUp() throws Exception { + log.debug("SET UP"); + super.setUp(); + jra = new JcrResourceAdapter(); + jra.setSession(session()); + } + + @Override + protected void tearDown() throws Exception { + log.debug("TEAR DOWN"); + super.tearDown(); + } +} diff --git a/org.argeo.jcr/ext/test/org/argeo/server/jcr/dummy00.xls b/org.argeo.jcr/ext/test/org/argeo/server/jcr/dummy00.xls new file mode 100644 index 000000000..e5846fef7 Binary files /dev/null and b/org.argeo.jcr/ext/test/org/argeo/server/jcr/dummy00.xls differ diff --git a/org.argeo.jcr/ext/test/org/argeo/server/jcr/dummy01.xls b/org.argeo.jcr/ext/test/org/argeo/server/jcr/dummy01.xls new file mode 100644 index 000000000..b5c6b5539 Binary files /dev/null and b/org.argeo.jcr/ext/test/org/argeo/server/jcr/dummy01.xls differ diff --git a/org.argeo.jcr/ext/test/org/argeo/server/jcr/dummy02.xls b/org.argeo.jcr/ext/test/org/argeo/server/jcr/dummy02.xls new file mode 100644 index 000000000..d73bc6605 Binary files /dev/null and b/org.argeo.jcr/ext/test/org/argeo/server/jcr/dummy02.xls differ diff --git a/org.argeo.jcr/ext/test/org/argeo/server/jcr/dummy03.xls b/org.argeo.jcr/ext/test/org/argeo/server/jcr/dummy03.xls new file mode 100644 index 000000000..0759cb927 Binary files /dev/null and b/org.argeo.jcr/ext/test/org/argeo/server/jcr/dummy03.xls differ diff --git a/org.argeo.jcr/pom.xml b/org.argeo.jcr/pom.xml new file mode 100644 index 000000000..0abd44cec --- /dev/null +++ b/org.argeo.jcr/pom.xml @@ -0,0 +1,18 @@ + + 4.0.0 + + org.argeo.commons + argeo-commons + 2.1.46-SNAPSHOT + .. + + org.argeo.jcr + Commons JCR + + + org.argeo.commons + org.argeo.util + 2.1.46-SNAPSHOT + + + \ No newline at end of file diff --git a/org.argeo.jcr/src/org/argeo/jackrabbit/JackrabbitAdminLoginModule.java b/org.argeo.jcr/src/org/argeo/jackrabbit/JackrabbitAdminLoginModule.java new file mode 100644 index 000000000..7e17dbca1 --- /dev/null +++ b/org.argeo.jcr/src/org/argeo/jackrabbit/JackrabbitAdminLoginModule.java @@ -0,0 +1,47 @@ +package org.argeo.jackrabbit; + +import java.util.Map; + +import javax.security.auth.Subject; +import javax.security.auth.callback.CallbackHandler; +import javax.security.auth.login.LoginException; +import javax.security.auth.spi.LoginModule; + +import org.apache.jackrabbit.core.security.SecurityConstants; +import org.apache.jackrabbit.core.security.principal.AdminPrincipal; + +public class JackrabbitAdminLoginModule implements LoginModule { + private Subject subject; + + @Override + public void initialize(Subject subject, CallbackHandler callbackHandler, + Map sharedState, Map options) { + this.subject = subject; + } + + @Override + public boolean login() throws LoginException { + // TODO check permission? + return true; + } + + @Override + public boolean commit() throws LoginException { + subject.getPrincipals().add( + new AdminPrincipal(SecurityConstants.ADMIN_ID)); + return true; + } + + @Override + public boolean abort() throws LoginException { + return true; + } + + @Override + public boolean logout() throws LoginException { + subject.getPrincipals().removeAll( + subject.getPrincipals(AdminPrincipal.class)); + return true; + } + +} diff --git a/org.argeo.jcr/src/org/argeo/jackrabbit/JackrabbitContainer.java b/org.argeo.jcr/src/org/argeo/jackrabbit/JackrabbitContainer.java new file mode 100644 index 000000000..c5ca7952c --- /dev/null +++ b/org.argeo.jcr/src/org/argeo/jackrabbit/JackrabbitContainer.java @@ -0,0 +1,351 @@ +/* + * 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.jcr.ArgeoJcrException; +import org.argeo.jcr.ArgeoNames; +import org.argeo.jcr.JcrUtils; +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}. + */ +@Deprecated +public class JackrabbitContainer extends JackrabbitWrapper { + 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 dataModelMigrations = new HashSet(); + + /** 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 ArgeoJcrException("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 ArgeoJcrException("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 ArgeoJcrException("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( + dataModelMigrations)) { + if (dataModelMigration.migrate(session)) { + restartAndClearCaches = true; + } + } + } catch (ArgeoJcrException e) { + throw e; + } catch (Exception e) { + throw new ArgeoJcrException("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 ArgeoJcrException("Cannot login to migrated repository", e); + } + + for (JackrabbitDataModelMigration dataModelMigration : new TreeSet( + 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; + if (log.isTraceEnabled()) + log.trace("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 ArgeoJcrException("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 ArgeoJcrException("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 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(vars.keySet())) + log.trace(key + "=" + vars.getProperty(key.toString())); + } + + } catch (IOException e) { + throw new ArgeoJcrException("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 ArgeoJcrException("Cannot be used to wrap another repository"); + } + + public void setDataModelMigrations(Set 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; + } + +} diff --git a/org.argeo.jcr/src/org/argeo/jackrabbit/JackrabbitDataModelMigration.java b/org.argeo.jcr/src/org/argeo/jackrabbit/JackrabbitDataModelMigration.java new file mode 100644 index 000000000..53f0e4457 --- /dev/null +++ b/org.argeo.jcr/src/org/argeo/jackrabbit/JackrabbitDataModelMigration.java @@ -0,0 +1,183 @@ +/* + * 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.jcr.ArgeoJcrException; +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 { + 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 ArgeoJcrException("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"; + // FIXME causes weird error in Eclipse + //repositoryConfig.getFileSystem().deleteFile(customeNodeTypesPath); + if (log.isDebugEnabled()) + log.debug("Cleared " + customeNodeTypesPath); + } catch (Exception e) { + throw new ArgeoJcrException("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; + } + +} diff --git a/org.argeo.jcr/src/org/argeo/jackrabbit/JackrabbitRepositoryFactory.java b/org.argeo.jcr/src/org/argeo/jackrabbit/JackrabbitRepositoryFactory.java new file mode 100644 index 000000000..e4134883c --- /dev/null +++ b/org.argeo.jcr/src/org/argeo/jackrabbit/JackrabbitRepositoryFactory.java @@ -0,0 +1,178 @@ +/* + * 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.net.URI; +import java.net.URISyntaxException; +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.jcr.ArgeoJcrConstants; +import org.argeo.jcr.ArgeoJcrException; +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 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" }) + 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 + Repository repository; + 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)); + } else + throw new ArgeoJcrException("Unrecognized URI format " + uri); + + } + + else 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); + repository = getRepositoryByAlias(alias); + } else + throw new ArgeoJcrException("Not enough information in " + parameters); + + if (repository == null) + throw new ArgeoJcrException("Repository not found " + parameters); + + return repository; + } + + protected Repository getRepositoryByAlias(String alias) { + return null; + } + + protected Repository createRemoteRepository(String uri) throws RepositoryException { + Map params = new HashMap(); + params.put(JcrUtils.REPOSITORY_URI, uri); + Repository repository = new Jcr2davRepositoryFactory().getRepository(params); + if (repository == null) + throw new ArgeoJcrException("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 ArgeoJcrException("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 ArgeoJcrException("Cannot create repository " + uri, e); + } finally { + IOUtils.closeQuietly(configurationIn); + } + } + + protected String getAliasFromURI(String uri) { + try { + URI uriObj = new URI(uri); + String alias = uriObj.getPath(); + if (alias.charAt(0) == '/') + alias = alias.substring(1); + if (alias.charAt(alias.length() - 1) == '/') + alias = alias.substring(0, alias.length() - 1); + return alias; + } catch (URISyntaxException e) { + throw new ArgeoJcrException("Cannot interpret URI " + uri, e); + } + } + + /** + * 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; + } + +} diff --git a/org.argeo.jcr/src/org/argeo/jackrabbit/JackrabbitWrapper.java b/org.argeo.jcr/src/org/argeo/jackrabbit/JackrabbitWrapper.java new file mode 100644 index 000000000..268ecdb28 --- /dev/null +++ b/org.argeo.jcr/src/org/argeo/jackrabbit/JackrabbitWrapper.java @@ -0,0 +1,397 @@ +/* + * 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.LoginException; +import javax.jcr.NoSuchWorkspaceException; +import javax.jcr.Node; +import javax.jcr.NodeIterator; +import javax.jcr.Repository; +import javax.jcr.RepositoryException; +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.api.JackrabbitRepository; +import org.apache.jackrabbit.commons.NamespaceHelper; +import org.apache.jackrabbit.commons.cnd.CndImporter; +import org.argeo.jcr.ArgeoJcrConstants; +import org.argeo.jcr.ArgeoJcrException; +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}. + */ +@Deprecated +public class JackrabbitWrapper extends JcrRepositoryWrapper implements + JackrabbitRepository, 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 cndFiles = new ArrayList(); + /** + * 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 namespaces = new HashMap(); + + 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 ArgeoJcrException("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.isTraceEnabled()) + log.trace("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 ArgeoJcrException("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 ArgeoJcrException("No " + resUrl + " in the classpath," + + " make sure the containing" + " package is visible."); + } + + return IOUtils.toByteArray(in); + } catch (Exception e) { + throw new ArgeoJcrException("Cannot read CND from " + resUrl, e); + } finally { + IOUtils.closeQuietly(in); + } + } + + /* + * JACKRABBIT REPOSITORY IMPLEMENTATION + */ + @Override + public Session login(Credentials credentials, String workspaceName, + Map attributes) throws LoginException, + NoSuchWorkspaceException, RepositoryException { + // TODO Auto-generated method stub + return null; + } + + @Override + public void shutdown() { + // TODO Auto-generated method stub + + } + + /* + * 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); + PackageAdmin packageAdmin = (PackageAdmin) bundleContext + .getService(paSr); + + // find exported package + ExportedPackage exportedPackage = null; + ExportedPackage[] exportedPackages = packageAdmin + .getExportedPackages(pkg); + if (exportedPackages == null) + throw new ArgeoJcrException("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 ArgeoJcrException("No OSGi exporting package found for " + // + resUrl); + } + return exportingBundle; + } + + /* + * FIELDS ACCESS + */ + public void setNamespaces(Map namespaces) { + this.namespaces = namespaces; + } + + public void setCndFiles(List 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; + } + +} diff --git a/org.argeo.jcr/src/org/argeo/jackrabbit/repository-h2.xml b/org.argeo.jcr/src/org/argeo/jackrabbit/repository-h2.xml new file mode 100644 index 000000000..05267621f --- /dev/null +++ b/org.argeo.jcr/src/org/argeo/jackrabbit/repository-h2.xml @@ -0,0 +1,82 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/org.argeo.jcr/src/org/argeo/jackrabbit/repository-localfs.xml b/org.argeo.jcr/src/org/argeo/jackrabbit/repository-localfs.xml new file mode 100644 index 000000000..3d2470863 --- /dev/null +++ b/org.argeo.jcr/src/org/argeo/jackrabbit/repository-localfs.xml @@ -0,0 +1,60 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/org.argeo.jcr/src/org/argeo/jackrabbit/repository-memory.xml b/org.argeo.jcr/src/org/argeo/jackrabbit/repository-memory.xml new file mode 100644 index 000000000..ecee5bdad --- /dev/null +++ b/org.argeo.jcr/src/org/argeo/jackrabbit/repository-memory.xml @@ -0,0 +1,55 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/org.argeo.jcr/src/org/argeo/jackrabbit/repository-postgresql-ds.xml b/org.argeo.jcr/src/org/argeo/jackrabbit/repository-postgresql-ds.xml new file mode 100644 index 000000000..07a0d0428 --- /dev/null +++ b/org.argeo.jcr/src/org/argeo/jackrabbit/repository-postgresql-ds.xml @@ -0,0 +1,82 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/org.argeo.jcr/src/org/argeo/jackrabbit/repository-postgresql.xml b/org.argeo.jcr/src/org/argeo/jackrabbit/repository-postgresql.xml new file mode 100644 index 000000000..967782820 --- /dev/null +++ b/org.argeo.jcr/src/org/argeo/jackrabbit/repository-postgresql.xml @@ -0,0 +1,79 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/org.argeo.jcr/src/org/argeo/jackrabbit/servlet/OpenInViewSessionProvider.java b/org.argeo.jcr/src/org/argeo/jackrabbit/servlet/OpenInViewSessionProvider.java new file mode 100644 index 000000000..52a9883ee --- /dev/null +++ b/org.argeo.jcr/src/org/argeo/jackrabbit/servlet/OpenInViewSessionProvider.java @@ -0,0 +1,70 @@ +/* + * 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 + */ +@Deprecated +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 ? "" : 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() { + } + +} diff --git a/org.argeo.jcr/src/org/argeo/jackrabbit/servlet/RemotingServlet.java b/org.argeo.jcr/src/org/argeo/jackrabbit/servlet/RemotingServlet.java new file mode 100644 index 000000000..3fdb5d2e1 --- /dev/null +++ b/org.argeo.jcr/src/org/argeo/jackrabbit/servlet/RemotingServlet.java @@ -0,0 +1,52 @@ +/* + * 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 */ +@Deprecated +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; + public final static String INIT_PARAM_PROTECTED_HANDLERS_CONFIG = JcrRemotingServlet.INIT_PARAM_PROTECTED_HANDLERS_CONFIG; + + 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; + } + +} diff --git a/org.argeo.jcr/src/org/argeo/jackrabbit/servlet/WebdavServlet.java b/org.argeo.jcr/src/org/argeo/jackrabbit/servlet/WebdavServlet.java new file mode 100644 index 000000000..e3176b742 --- /dev/null +++ b/org.argeo.jcr/src/org/argeo/jackrabbit/servlet/WebdavServlet.java @@ -0,0 +1,63 @@ +/* + * 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 */ +@Deprecated +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; + } + +} diff --git a/org.argeo.jcr/src/org/argeo/jackrabbit/unit/AbstractJackrabbitTestCase.java b/org.argeo.jcr/src/org/argeo/jackrabbit/unit/AbstractJackrabbitTestCase.java new file mode 100644 index 000000000..1523c835d --- /dev/null +++ b/org.argeo.jcr/src/org/argeo/jackrabbit/unit/AbstractJackrabbitTestCase.java @@ -0,0 +1,66 @@ +/* + * 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.unit; + +import java.net.URL; + +import javax.jcr.Repository; + +import org.apache.commons.io.FileUtils; +import org.apache.jackrabbit.core.RepositoryImpl; +import org.apache.jackrabbit.core.config.RepositoryConfig; +import org.argeo.jcr.unit.AbstractJcrTestCase; + +/** Factorizes configuration of an in memory transient repository */ +public abstract class AbstractJackrabbitTestCase extends AbstractJcrTestCase { + protected RepositoryImpl repositoryImpl; + + // protected File getRepositoryFile() throws Exception { + // Resource res = new ClassPathResource( + // "org/argeo/jackrabbit/unit/repository-memory.xml"); + // return res.getFile(); + // } + + public AbstractJackrabbitTestCase() { + URL url = AbstractJackrabbitTestCase.class.getResource("jaas.config"); + assert url != null; + System.setProperty("java.security.auth.login.config", url.toString()); + } + + protected Repository createRepository() throws Exception { + // Repository repository = new TransientRepository(getRepositoryFile(), + // getHomeDir()); + RepositoryConfig repositoryConfig = RepositoryConfig.create( + AbstractJackrabbitTestCase.class + .getResourceAsStream(getRepositoryConfigResource()), + getHomeDir().getAbsolutePath()); + RepositoryImpl repositoryImpl = RepositoryImpl.create(repositoryConfig); + return repositoryImpl; + } + + protected String getRepositoryConfigResource() { + return "repository-memory.xml"; + } + + @Override + protected void clearRepository(Repository repository) throws Exception { + RepositoryImpl repositoryImpl = (RepositoryImpl) repository; + if (repositoryImpl != null) + repositoryImpl.shutdown(); + FileUtils.deleteDirectory(getHomeDir()); + } + +} diff --git a/org.argeo.jcr/src/org/argeo/jackrabbit/unit/jaas.config b/org.argeo.jcr/src/org/argeo/jackrabbit/unit/jaas.config new file mode 100644 index 000000000..11d2f96cf --- /dev/null +++ b/org.argeo.jcr/src/org/argeo/jackrabbit/unit/jaas.config @@ -0,0 +1,7 @@ +TEST_JACKRABBIT_ADMIN { + org.argeo.jackrabbit.JackrabbitAdminLoginModule requisite; +}; + +Jackrabbit { + org.argeo.security.jackrabbit.SystemJackrabbitLoginModule requisite; +}; diff --git a/org.argeo.jcr/src/org/argeo/jackrabbit/unit/repository-h2.xml b/org.argeo.jcr/src/org/argeo/jackrabbit/unit/repository-h2.xml new file mode 100644 index 000000000..348dc288b --- /dev/null +++ b/org.argeo.jcr/src/org/argeo/jackrabbit/unit/repository-h2.xml @@ -0,0 +1,81 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/org.argeo.jcr/src/org/argeo/jackrabbit/unit/repository-memory.xml b/org.argeo.jcr/src/org/argeo/jackrabbit/unit/repository-memory.xml new file mode 100644 index 000000000..839542417 --- /dev/null +++ b/org.argeo.jcr/src/org/argeo/jackrabbit/unit/repository-memory.xml @@ -0,0 +1,72 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/org.argeo.jcr/src/org/argeo/jcr/ArgeoJcrConstants.java b/org.argeo.jcr/src/org/argeo/jcr/ArgeoJcrConstants.java new file mode 100644 index 000000000..26979e927 --- /dev/null +++ b/org.argeo.jcr/src/org/argeo/jcr/ArgeoJcrConstants.java @@ -0,0 +1,43 @@ +/* + * 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.jcr; + +import javax.jcr.Repository; + +/** Argeo model specific constants */ +public interface ArgeoJcrConstants { + public final static String ARGEO_BASE_PATH = "/argeo:system"; + public final static String DATA_MODELS_BASE_PATH = ARGEO_BASE_PATH + "/argeo:dataModels"; + public final static String PEOPLE_BASE_PATH = ARGEO_BASE_PATH + "/argeo:people"; + + // parameters (typically for call to a RepositoryFactory) + /** Key for a JCR repository alias */ + public final static String JCR_REPOSITORY_ALIAS = "argeo.jcr.repository.alias"; + /** Key for a JCR repository URI */ + public final static String JCR_REPOSITORY_URI = "argeo.jcr.repository.uri"; + + // standard aliases + /** + * Reserved alias for the "node" {@link Repository}, that is, the default + * JCR repository. + */ + public final static String ALIAS_NODE = "node"; + public final static String ALIAS_HOME = "home"; + public final static String BASE_REPO_PID = "argeo.repo."; + public final static String REPO_PID_NODE = BASE_REPO_PID + ALIAS_NODE; + public final static String JACKRABBIT_REPO_FACTORY_PID = "argeo.repo.factory.jackrabbit"; + +} diff --git a/org.argeo.jcr/src/org/argeo/jcr/ArgeoJcrException.java b/org.argeo.jcr/src/org/argeo/jcr/ArgeoJcrException.java new file mode 100644 index 000000000..8e19593dd --- /dev/null +++ b/org.argeo.jcr/src/org/argeo/jcr/ArgeoJcrException.java @@ -0,0 +1,15 @@ +package org.argeo.jcr; + +/** Argeo JCR specific exceptions. */ +public class ArgeoJcrException extends RuntimeException { + private static final long serialVersionUID = -1941940005390084331L; + + public ArgeoJcrException(String message, Throwable cause) { + super(message, cause); + } + + public ArgeoJcrException(String message) { + super(message); + } + +} diff --git a/org.argeo.jcr/src/org/argeo/jcr/ArgeoJcrUtils.java b/org.argeo.jcr/src/org/argeo/jcr/ArgeoJcrUtils.java new file mode 100644 index 000000000..9a0fd1971 --- /dev/null +++ b/org.argeo.jcr/src/org/argeo/jcr/ArgeoJcrUtils.java @@ -0,0 +1,78 @@ +/* + * 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.jcr; + +import java.util.HashMap; +import java.util.Map; + +import javax.jcr.Repository; +import javax.jcr.RepositoryException; +import javax.jcr.RepositoryFactory; + +/** Utilities related to Argeo model in JCR */ +public class ArgeoJcrUtils implements ArgeoJcrConstants { + /** + * Wraps the call to the repository factory based on parameter + * {@link ArgeoJcrConstants#JCR_REPOSITORY_ALIAS} in order to simplify it + * and protect against future API changes. + */ + public static Repository getRepositoryByAlias( + RepositoryFactory repositoryFactory, String alias) { + try { + Map parameters = new HashMap(); + parameters.put(JCR_REPOSITORY_ALIAS, alias); + return repositoryFactory.getRepository(parameters); + } catch (RepositoryException e) { + throw new ArgeoJcrException( + "Unexpected exception when trying to retrieve repository with alias " + + alias, e); + } + } + + /** + * Wraps the call to the repository factory based on parameter + * {@link ArgeoJcrConstants#JCR_REPOSITORY_URI} in order to simplify it and + * protect against future API changes. + */ + public static Repository getRepositoryByUri( + RepositoryFactory repositoryFactory, String uri) { + return getRepositoryByUri(repositoryFactory, uri, null); + } + + /** + * Wraps the call to the repository factory based on parameter + * {@link ArgeoJcrConstants#JCR_REPOSITORY_URI} in order to simplify it and + * protect against future API changes. + */ + public static Repository getRepositoryByUri( + RepositoryFactory repositoryFactory, String uri, String alias) { + try { + Map parameters = new HashMap(); + parameters.put(JCR_REPOSITORY_URI, uri); + if (alias != null) + parameters.put(JCR_REPOSITORY_ALIAS, alias); + return repositoryFactory.getRepository(parameters); + } catch (RepositoryException e) { + throw new ArgeoJcrException( + "Unexpected exception when trying to retrieve repository with uri " + + uri, e); + } + } + + private ArgeoJcrUtils() { + } + +} diff --git a/org.argeo.jcr/src/org/argeo/jcr/ArgeoNames.java b/org.argeo.jcr/src/org/argeo/jcr/ArgeoNames.java new file mode 100644 index 000000000..1d4582a78 --- /dev/null +++ b/org.argeo.jcr/src/org/argeo/jcr/ArgeoNames.java @@ -0,0 +1,59 @@ +/* + * 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.jcr; + +/** JCR names in the http://www.argeo.org/argeo namespace */ +public interface ArgeoNames { + public final static String ARGEO_NAMESPACE = "http://www.argeo.org/ns/argeo"; + public final static String ARGEO = "argeo"; + + public final static String ARGEO_URI = "argeo:uri"; + public final static String ARGEO_USER_ID = "argeo:userID"; + public final static String ARGEO_PREFERENCES = "argeo:preferences"; + public final static String ARGEO_DATA_MODEL_VERSION = "argeo:dataModelVersion"; + + public final static String ARGEO_REMOTE = "argeo:remote"; + public final static String ARGEO_PASSWORD = "argeo:password"; +// public final static String ARGEO_REMOTE_ROLES = "argeo:remoteRoles"; + + // user profile + public final static String ARGEO_PROFILE = "argeo:profile"; + + // spring security + public final static String ARGEO_ENABLED = "argeo:enabled"; + public final static String ARGEO_ACCOUNT_NON_EXPIRED = "argeo:accountNonExpired"; + public final static String ARGEO_ACCOUNT_NON_LOCKED = "argeo:accountNonLocked"; + public final static String ARGEO_CREDENTIALS_NON_EXPIRED = "argeo:credentialsNonExpired"; + + // personal details + public final static String ARGEO_FIRST_NAME = "argeo:firstName"; + public final static String ARGEO_LAST_NAME = "argeo:lastName"; + public final static String ARGEO_PRIMARY_EMAIL = "argeo:primaryEmail"; + public final static String ARGEO_PRIMARY_ORGANIZATION = "argeo:primaryOrganization"; + + // tabular + public final static String ARGEO_IS_KEY = "argeo:isKey"; + + // crypto + public final static String ARGEO_IV = "argeo:iv"; + public final static String ARGEO_SECRET_KEY_FACTORY = "argeo:secretKeyFactory"; + public final static String ARGEO_SALT = "argeo:salt"; + public final static String ARGEO_ITERATION_COUNT = "argeo:iterationCount"; + public final static String ARGEO_KEY_LENGTH = "argeo:keyLength"; + public final static String ARGEO_SECRET_KEY_ENCRYPTION = "argeo:secretKeyEncryption"; + public final static String ARGEO_CIPHER = "argeo:cipher"; + public final static String ARGEO_KEYRING = "argeo:keyring"; +} diff --git a/org.argeo.jcr/src/org/argeo/jcr/ArgeoTypes.java b/org.argeo.jcr/src/org/argeo/jcr/ArgeoTypes.java new file mode 100644 index 000000000..a11ead53f --- /dev/null +++ b/org.argeo.jcr/src/org/argeo/jcr/ArgeoTypes.java @@ -0,0 +1,38 @@ +/* + * 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.jcr; + +/** JCR types in the http://www.argeo.org/argeo namespace */ +public interface ArgeoTypes { + public final static String ARGEO_LINK = "argeo:link"; + public final static String ARGEO_USER_HOME = "argeo:userHome"; + public final static String ARGEO_USER_PROFILE = "argeo:userProfile"; + public final static String ARGEO_REMOTE_REPOSITORY = "argeo:remoteRepository"; + public final static String ARGEO_PREFERENCE_NODE = "argeo:preferenceNode"; + + // data model + public final static String ARGEO_DATA_MODEL = "argeo:dataModel"; + + // tabular + public final static String ARGEO_TABLE = "argeo:table"; + public final static String ARGEO_COLUMN = "argeo:column"; + public final static String ARGEO_CSV = "argeo:csv"; + + // crypto + public final static String ARGEO_ENCRYPTED = "argeo:encrypted"; + public final static String ARGEO_PBE_SPEC = "argeo:pbeSpec"; + +} diff --git a/org.argeo.jcr/src/org/argeo/jcr/CollectionNodeIterator.java b/org.argeo.jcr/src/org/argeo/jcr/CollectionNodeIterator.java new file mode 100644 index 000000000..a65907a7a --- /dev/null +++ b/org.argeo.jcr/src/org/argeo/jcr/CollectionNodeIterator.java @@ -0,0 +1,76 @@ +/* + * 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.jcr; + +import java.util.Collection; +import java.util.Iterator; +import java.util.NoSuchElementException; + +import javax.jcr.Node; +import javax.jcr.NodeIterator; + +/** Wraps a collection of nodes in order to read it as a {@link NodeIterator} */ +public class CollectionNodeIterator implements NodeIterator { + private final Long collectionSize; + private final Iterator iterator; + private Integer position = 0; + + public CollectionNodeIterator(Collection nodes) { + super(); + this.collectionSize = (long) nodes.size(); + this.iterator = nodes.iterator(); + } + + public void skip(long skipNum) { + if (skipNum < 0) + throw new IllegalArgumentException( + "Skip count has to be positive: " + skipNum); + + for (long i = 0; i < skipNum; i++) { + if (!hasNext()) + throw new NoSuchElementException("Last element past (position=" + + getPosition() + ")"); + nextNode(); + } + } + + public long getSize() { + return collectionSize; + } + + public long getPosition() { + return position; + } + + public boolean hasNext() { + return iterator.hasNext(); + } + + public Object next() { + return nextNode(); + } + + public void remove() { + iterator.remove(); + } + + public Node nextNode() { + Node node = iterator.next(); + position++; + return node; + } + +} diff --git a/org.argeo.jcr/src/org/argeo/jcr/DefaultJcrListener.java b/org.argeo.jcr/src/org/argeo/jcr/DefaultJcrListener.java new file mode 100644 index 000000000..5ef8edd56 --- /dev/null +++ b/org.argeo.jcr/src/org/argeo/jcr/DefaultJcrListener.java @@ -0,0 +1,93 @@ +/* + * 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.jcr; + +import javax.jcr.RepositoryException; +import javax.jcr.Session; +import javax.jcr.observation.Event; +import javax.jcr.observation.EventIterator; +import javax.jcr.observation.EventListener; +import javax.jcr.observation.ObservationManager; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** To be overridden */ +public class DefaultJcrListener implements EventListener { + private final static Log log = LogFactory.getLog(DefaultJcrListener.class); + private Session session; + private String path = "/"; + private Boolean deep = true; + + public void start() { + try { + addEventListener(session().getWorkspace().getObservationManager()); + if (log.isDebugEnabled()) + log.debug("Registered JCR event listener on " + path); + } catch (Exception e) { + throw new ArgeoJcrException("Cannot register event listener", e); + } + } + + public void stop() { + try { + session().getWorkspace().getObservationManager() + .removeEventListener(this); + if (log.isDebugEnabled()) + log.debug("Unregistered JCR event listener on " + path); + } catch (Exception e) { + throw new ArgeoJcrException("Cannot unregister event listener", e); + } + } + + /** Default is listen to all events */ + protected Integer getEvents() { + return Event.NODE_ADDED | Event.NODE_REMOVED | Event.PROPERTY_ADDED + | Event.PROPERTY_CHANGED | Event.PROPERTY_REMOVED; + } + + /** To be overidden */ + public void onEvent(EventIterator events) { + while (events.hasNext()) { + Event event = events.nextEvent(); + log.debug(event); + } + } + + /** To be overidden */ + protected void addEventListener(ObservationManager observationManager) + throws RepositoryException { + observationManager.addEventListener(this, getEvents(), path, deep, + null, null, false); + } + + private Session session() { + return session; + } + + public void setPath(String path) { + this.path = path; + } + + public void setDeep(Boolean deep) { + this.deep = deep; + } + + public void setSession(Session session) { + this.session = session; + } + +} diff --git a/org.argeo.jcr/src/org/argeo/jcr/DefaultRepositoryFactory.java b/org.argeo.jcr/src/org/argeo/jcr/DefaultRepositoryFactory.java new file mode 100644 index 000000000..ccfc2698e --- /dev/null +++ b/org.argeo.jcr/src/org/argeo/jcr/DefaultRepositoryFactory.java @@ -0,0 +1,76 @@ +/* + * 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.jcr; + +import java.net.URI; +import java.net.URISyntaxException; +import java.util.Map; +import java.util.Properties; + +import javax.jcr.Repository; +import javax.jcr.RepositoryException; +import javax.jcr.RepositoryFactory; + +/** + * Simple implementation of {@link RepositoryFactory}, supporting OSGi aliases. + */ +@Deprecated +public class DefaultRepositoryFactory extends DefaultRepositoryRegister + implements RepositoryFactory, ArgeoJcrConstants { + @SuppressWarnings("rawtypes") + public Repository getRepository(Map parameters) throws RepositoryException { + if (parameters.containsKey(JCR_REPOSITORY_ALIAS)) { + String alias = parameters.get(JCR_REPOSITORY_ALIAS).toString(); + return getRepositoryByAlias(alias); + } else if (parameters.containsKey(JCR_REPOSITORY_URI)) { + String uri = parameters.get(JCR_REPOSITORY_URI).toString(); + return getRepositoryByAlias(getAliasFromURI(uri)); + } + return null; + } + + protected String getAliasFromURI(String uri) { + try { + URI uriObj = new URI(uri); + String alias = uriObj.getPath(); + if (alias.charAt(0) == '/') + alias = alias.substring(1); + if (alias.charAt(alias.length() - 1) == '/') + alias = alias.substring(0, alias.length() - 1); + return alias; + } catch (URISyntaxException e) { + throw new ArgeoJcrException("Cannot interpret URI " + uri, e); + } + } + + /** + * Retrieve a repository by alias + * + * @return the repository registered with alias or null if none + */ + protected Repository getRepositoryByAlias(String alias) { + if (getRepositories().containsKey(alias)) + return getRepositories().get(alias); + else + return null; + } + + protected void publish(String alias, Repository repository, + Properties properties) { + register(repository, properties); + } + +} diff --git a/org.argeo.jcr/src/org/argeo/jcr/DefaultRepositoryRegister.java b/org.argeo.jcr/src/org/argeo/jcr/DefaultRepositoryRegister.java new file mode 100644 index 000000000..e2a5026d8 --- /dev/null +++ b/org.argeo.jcr/src/org/argeo/jcr/DefaultRepositoryRegister.java @@ -0,0 +1,97 @@ +/* + * 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.jcr; + +import java.util.Collections; +import java.util.Map; +import java.util.Observable; +import java.util.TreeMap; + +import javax.jcr.Repository; +import javax.jcr.RepositoryException; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +@Deprecated +public class DefaultRepositoryRegister extends Observable implements + RepositoryRegister, ArgeoJcrConstants { + private final static Log log = LogFactory + .getLog(DefaultRepositoryRegister.class); + + /** Read only map which will be directly exposed. */ + private Map repositories = Collections + .unmodifiableMap(new TreeMap()); + + @SuppressWarnings("rawtypes") + public synchronized Repository getRepository(Map parameters) + throws RepositoryException { + if (!parameters.containsKey(JCR_REPOSITORY_ALIAS)) + throw new RepositoryException("Parameter " + JCR_REPOSITORY_ALIAS + + " has to be defined."); + String alias = parameters.get(JCR_REPOSITORY_ALIAS).toString(); + if (!repositories.containsKey(alias)) + throw new RepositoryException( + "No repository registered with alias " + alias); + + return repositories.get(alias); + } + + /** Access to the read-only map */ + public synchronized Map getRepositories() { + return repositories; + } + + /** Registers a service, typically called when OSGi services are bound. */ + @SuppressWarnings("rawtypes") + public synchronized void register(Repository repository, Map properties) { + String alias; + if (properties == null || !properties.containsKey(JCR_REPOSITORY_ALIAS)) { + log.warn("Cannot register a repository if no " + + JCR_REPOSITORY_ALIAS + " property is specified."); + return; + } + alias = properties.get(JCR_REPOSITORY_ALIAS).toString(); + Map map = new TreeMap( + repositories); + map.put(alias, repository); + repositories = Collections.unmodifiableMap(map); + setChanged(); + notifyObservers(alias); + } + + /** Unregisters a service, typically called when OSGi services are unbound. */ + @SuppressWarnings("rawtypes") + public synchronized void unregister(Repository repository, Map properties) { + // TODO: also check bean name? + if (properties == null || !properties.containsKey(JCR_REPOSITORY_ALIAS)) { + log.warn("Cannot unregister a repository without property " + + JCR_REPOSITORY_ALIAS); + return; + } + + String alias = properties.get(JCR_REPOSITORY_ALIAS).toString(); + Map map = new TreeMap( + repositories); + if (map.remove(alias) == null) { + log.warn("No repository was registered with alias " + alias); + return; + } + repositories = Collections.unmodifiableMap(map); + setChanged(); + notifyObservers(alias); + } +} diff --git a/org.argeo.jcr/src/org/argeo/jcr/JcrCallback.java b/org.argeo.jcr/src/org/argeo/jcr/JcrCallback.java new file mode 100644 index 000000000..0c4706f84 --- /dev/null +++ b/org.argeo.jcr/src/org/argeo/jcr/JcrCallback.java @@ -0,0 +1,23 @@ +/* + * 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.jcr; + +import javax.jcr.Session; + +/** An arbitrary execution on a JCR session, optionally returning a result. */ +public interface JcrCallback { + public Object execute(Session session); +} diff --git a/org.argeo.jcr/src/org/argeo/jcr/JcrMonitor.java b/org.argeo.jcr/src/org/argeo/jcr/JcrMonitor.java new file mode 100644 index 000000000..f04be9aa5 --- /dev/null +++ b/org.argeo.jcr/src/org/argeo/jcr/JcrMonitor.java @@ -0,0 +1,89 @@ +package org.argeo.jcr; + +import org.argeo.ArgeoMonitor; + +/** + * Simple monitor abstraction. Inspired by Eclipse IProgressMOnitor, but without + * dependency to it. + */ +@SuppressWarnings("deprecation") +public interface JcrMonitor extends ArgeoMonitor { + /** + * Constant indicating an unknown amount of work. + */ + public final static int UNKNOWN = -1; + + /** + * Notifies that the main task is beginning. This must only be called once + * on a given progress monitor instance. + * + * @param name + * the name (or description) of the main task + * @param totalWork + * the total number of work units into which the main task is + * been subdivided. If the value is UNKNOWN the + * implementation is free to indicate progress in a way which + * doesn't require the total number of work units in advance. + */ + public void beginTask(String name, int totalWork); + + /** + * Notifies that the work is done; that is, either the main task is + * completed or the user canceled it. This method may be called more than + * once (implementations should be prepared to handle this case). + */ + public void done(); + + /** + * Returns whether cancelation of current operation has been requested. + * Long-running operations should poll to see if cancelation has been + * requested. + * + * @return true if cancellation has been requested, and + * false otherwise + * @see #setCanceled(boolean) + */ + public boolean isCanceled(); + + /** + * Sets the cancel state to the given value. + * + * @param value + * true indicates that cancelation has been + * requested (but not necessarily acknowledged); + * false clears this flag + * @see #isCanceled() + */ + public void setCanceled(boolean value); + + /** + * Sets the task name to the given value. This method is used to restore the + * task label after a nested operation was executed. Normally there is no + * need for clients to call this method. + * + * @param name + * the name (or description) of the main task + * @see #beginTask(java.lang.String, int) + */ + public void setTaskName(String name); + + /** + * Notifies that a subtask of the main task is beginning. Subtasks are + * optional; the main task might not have subtasks. + * + * @param name + * the name (or description) of the subtask + */ + public void subTask(String name); + + /** + * Notifies that a given number of work unit of the main task has been + * completed. Note that this amount represents an installment, as opposed to + * a cumulative amount of work done to date. + * + * @param work + * a non-negative number of work units just completed + */ + public void worked(int work); + +} diff --git a/org.argeo.jcr/src/org/argeo/jcr/JcrRepositoryWrapper.java b/org.argeo.jcr/src/org/argeo/jcr/JcrRepositoryWrapper.java new file mode 100644 index 000000000..6c23acaac --- /dev/null +++ b/org.argeo.jcr/src/org/argeo/jcr/JcrRepositoryWrapper.java @@ -0,0 +1,150 @@ +/* + * 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.jcr; + +import javax.jcr.Credentials; +import javax.jcr.LoginException; +import javax.jcr.NoSuchWorkspaceException; +import javax.jcr.Repository; +import javax.jcr.RepositoryException; +import javax.jcr.Session; +import javax.jcr.Value; + +/** + * Wrapper around a JCR repository which allows to simplify configuration and + * intercept some actions. It exposes itself as a {@link Repository}. + */ +public abstract class JcrRepositoryWrapper implements Repository { + // private final static Log log = LogFactory + // .getLog(JcrRepositoryWrapper.class); + + // wrapped repository + private Repository repository; + + private Boolean autocreateWorkspaces = false; + + /** + * Empty constructor, {@link #init()} should be called after properties have + * been set + */ + public JcrRepositoryWrapper() { + } + + /** Initializes */ + public void init() { + } + + /** Shutdown the repository */ + public void destroy() throws Exception { + } + + /* + * DELEGATED JCR REPOSITORY METHODS + */ + + public String getDescriptor(String key) { + return getRepository().getDescriptor(key); + } + + public String[] getDescriptorKeys() { + return getRepository().getDescriptorKeys(); + } + + /** Central login method */ + public Session login(Credentials credentials, String workspaceName) + throws LoginException, NoSuchWorkspaceException, + RepositoryException { + Session session; + try { + session = getRepository().login(credentials, workspaceName); + } catch (NoSuchWorkspaceException e) { + if (autocreateWorkspaces && workspaceName != null) + session = createWorkspaceAndLogsIn(credentials, workspaceName); + else + throw e; + } + processNewSession(session); + return session; + } + + public Session login() throws LoginException, RepositoryException { + return login(null, null); + } + + public Session login(Credentials credentials) throws LoginException, + RepositoryException { + return login(credentials, null); + } + + public Session login(String workspaceName) throws LoginException, + NoSuchWorkspaceException, RepositoryException { + return login(null, workspaceName); + } + + /** Called after a session has been created, does nothing by default. */ + protected void processNewSession(Session session) { + } + + /** Wraps access to the repository, making sure it is available. */ + protected synchronized Repository getRepository() { +// if (repository == null) { +// throw new ArgeoJcrException("No repository initialized." +// + " Was the init() method called?" +// + " The destroy() method should also" +// + " be called on shutdown."); +// } + return repository; + } + + /** + * Logs in to the default workspace, creates the required workspace, logs + * out, logs in to the required workspace. + */ + protected Session createWorkspaceAndLogsIn(Credentials credentials, + String workspaceName) throws RepositoryException { + if (workspaceName == null) + throw new ArgeoJcrException("No workspace specified."); + Session session = getRepository().login(credentials); + session.getWorkspace().createWorkspace(workspaceName); + session.logout(); + return getRepository().login(credentials, workspaceName); + } + + public boolean isStandardDescriptor(String key) { + return getRepository().isStandardDescriptor(key); + } + + public boolean isSingleValueDescriptor(String key) { + return getRepository().isSingleValueDescriptor(key); + } + + public Value getDescriptorValue(String key) { + return getRepository().getDescriptorValue(key); + } + + public Value[] getDescriptorValues(String key) { + return getRepository().getDescriptorValues(key); + } + + public synchronized void setRepository(Repository repository) { + this.repository = repository; + } + + public void setAutocreateWorkspaces(Boolean autocreateWorkspaces) { + this.autocreateWorkspaces = autocreateWorkspaces; + } + +} diff --git a/org.argeo.jcr/src/org/argeo/jcr/JcrResourceAdapter.java b/org.argeo.jcr/src/org/argeo/jcr/JcrResourceAdapter.java new file mode 100644 index 000000000..1ccce4f76 --- /dev/null +++ b/org.argeo.jcr/src/org/argeo/jcr/JcrResourceAdapter.java @@ -0,0 +1,244 @@ +/* + * 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.jcr; + +import java.io.InputStream; +import java.util.ArrayList; +import java.util.Calendar; +import java.util.List; + +import javax.jcr.Binary; +import javax.jcr.Node; +import javax.jcr.Property; +import javax.jcr.RepositoryException; +import javax.jcr.Session; +import javax.jcr.nodetype.NodeType; +import javax.jcr.version.Version; +import javax.jcr.version.VersionHistory; +import javax.jcr.version.VersionIterator; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * Bridge Spring resources and JCR folder / files semantics (nt:folder / + * nt:file), supporting versioning as well. + */ +public class JcrResourceAdapter { + private final static Log log = LogFactory.getLog(JcrResourceAdapter.class); + + private Session session; + + private Boolean versioning = true; + private String defaultEncoding = "UTF-8"; + + // private String restoreBase = "/.restore"; + + public JcrResourceAdapter() { + } + + public JcrResourceAdapter(Session session) { + this.session = session; + } + + public void mkdirs(String path) { + JcrUtils.mkdirs(session(), path, NodeType.NT_FOLDER, + NodeType.NT_FOLDER, versioning); + } + + public void create(String path, InputStream in, String mimeType) { + try { + if (session().itemExists(path)) { + throw new ArgeoJcrException("Node " + path + " already exists."); + } + + int index = path.lastIndexOf('/'); + String parentPath = path.substring(0, index); + if (parentPath.equals("")) + parentPath = "/"; + String fileName = path.substring(index + 1); + if (!session().itemExists(parentPath)) + throw new ArgeoJcrException("Parent folder of node " + path + + " does not exist: " + parentPath); + + Node folderNode = (Node) session().getItem(parentPath); + Node fileNode = folderNode.addNode(fileName, "nt:file"); + + Node contentNode = fileNode.addNode(Property.JCR_CONTENT, + "nt:resource"); + if (mimeType != null) + contentNode.setProperty(Property.JCR_MIMETYPE, mimeType); + contentNode.setProperty(Property.JCR_ENCODING, defaultEncoding); + Binary binary = session().getValueFactory().createBinary(in); + contentNode.setProperty(Property.JCR_DATA, binary); + JcrUtils.closeQuietly(binary); + Calendar lastModified = Calendar.getInstance(); + // lastModified.setTimeInMillis(file.lastModified()); + contentNode.setProperty(Property.JCR_LAST_MODIFIED, lastModified); + // resNode.addMixin("mix:referenceable"); + + if (versioning) + fileNode.addMixin("mix:versionable"); + + session().save(); + + if (versioning) + session().getWorkspace().getVersionManager() + .checkin(fileNode.getPath()); + + if (log.isDebugEnabled()) + log.debug("Created " + path); + } catch (Exception e) { + throw new ArgeoJcrException("Cannot create node for " + path, e); + } + + } + + public void update(String path, InputStream in) { + try { + + if (!session().itemExists(path)) { + String type = null; + // FIXME: using javax.activation leads to conflict between Java + // 1.5 and 1.6 (since javax.activation was included in Java 1.6) + // String type = new MimetypesFileTypeMap() + // .getContentType(FilenameUtils.getName(path)); + create(path, in, type); + return; + } + + Node fileNode = (Node) session().getItem(path); + Node contentNode = fileNode.getNode(Property.JCR_CONTENT); + if (versioning) + session().getWorkspace().getVersionManager() + .checkout(fileNode.getPath()); + Binary binary = session().getValueFactory().createBinary(in); + contentNode.setProperty(Property.JCR_DATA, binary); + JcrUtils.closeQuietly(binary); + Calendar lastModified = Calendar.getInstance(); + // lastModified.setTimeInMillis(file.lastModified()); + contentNode.setProperty(Property.JCR_LAST_MODIFIED, lastModified); + + session().save(); + if (versioning) + session().getWorkspace().getVersionManager() + .checkin(fileNode.getPath()); + + if (log.isDebugEnabled()) + log.debug("Updated " + path); + } catch (Exception e) { + throw new ArgeoJcrException("Cannot update node " + path, e); + } + } + + public List listVersions(String path) { + if (!versioning) + throw new ArgeoJcrException("Versioning is not activated"); + + try { + List versions = new ArrayList(); + Node fileNode = (Node) session().getItem(path); + VersionHistory history = session().getWorkspace() + .getVersionManager().getVersionHistory(fileNode.getPath()); + for (VersionIterator it = history.getAllVersions(); it.hasNext();) { + Version version = (Version) it.next(); + versions.add(version.getCreated()); + if (log.isTraceEnabled()) { + log.debug(version); + // debug(version); + } + } + return versions; + } catch (Exception e) { + throw new ArgeoJcrException("Cannot list version of node " + path, e); + } + } + + public InputStream retrieve(String path) { + try { + Node node = (Node) session().getItem( + path + "/" + Property.JCR_CONTENT); + Property property = node.getProperty(Property.JCR_DATA); + return property.getBinary().getStream(); + } catch (Exception e) { + throw new ArgeoJcrException("Cannot retrieve " + path, e); + } + } + + public synchronized InputStream retrieve(String path, Integer revision) { + if (!versioning) + throw new ArgeoJcrException("Versioning is not activated"); + + try { + Node fileNode = (Node) session().getItem(path); + VersionHistory history = session().getWorkspace() + .getVersionManager().getVersionHistory(fileNode.getPath()); + int count = 0; + Version version = null; + for (VersionIterator it = history.getAllVersions(); it.hasNext();) { + version = (Version) it.next(); + if (count == revision + 1) { + InputStream in = fromVersion(version); + if (log.isDebugEnabled()) + log.debug("Retrieved " + path + " at revision " + + revision); + return in; + } + count++; + } + } catch (Exception e) { + throw new ArgeoJcrException("Cannot retrieve version " + revision + + " of " + path, e); + } + + throw new ArgeoJcrException("Version " + revision + + " does not exist for node " + path); + } + + protected InputStream fromVersion(Version version) + throws RepositoryException { + Node frozenNode = version.getNode("jcr:frozenNode"); + InputStream in = frozenNode.getNode(Property.JCR_CONTENT) + .getProperty(Property.JCR_DATA).getBinary().getStream(); + return in; + } + + protected Session session() { + return session; + } + + public void setVersioning(Boolean versioning) { + this.versioning = versioning; + } + + public void setDefaultEncoding(String defaultEncoding) { + this.defaultEncoding = defaultEncoding; + } + + protected String fill(Integer number) { + int size = 4; + String str = number.toString(); + for (int i = str.length(); i < size; i++) { + str = "0" + str; + } + return str; + } + + public void setSession(Session session) { + this.session = session; + } + +} diff --git a/org.argeo.jcr/src/org/argeo/jcr/JcrUrlStreamHandler.java b/org.argeo.jcr/src/org/argeo/jcr/JcrUrlStreamHandler.java new file mode 100644 index 000000000..a77763937 --- /dev/null +++ b/org.argeo.jcr/src/org/argeo/jcr/JcrUrlStreamHandler.java @@ -0,0 +1,85 @@ +/* + * 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.jcr; + +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.net.URLConnection; +import java.net.URLStreamHandler; + +import javax.jcr.Item; +import javax.jcr.Node; +import javax.jcr.Property; +import javax.jcr.PropertyType; +import javax.jcr.RepositoryException; +import javax.jcr.Session; +import javax.jcr.nodetype.NodeType; + +/** URL stream handler able to deal with nt:file node and properties. NOT FINISHED */ +public class JcrUrlStreamHandler extends URLStreamHandler { + private final Session session; + + public JcrUrlStreamHandler(Session session) { + this.session = session; + } + + @Override + protected URLConnection openConnection(final URL u) throws IOException { + // TODO Auto-generated method stub + return new URLConnection(u) { + + @Override + public void connect() throws IOException { + String itemPath = u.getPath(); + try { + if (!session.itemExists(itemPath)) + throw new IOException("No item under " + itemPath); + + Item item = session.getItem(u.getPath()); + if (item.isNode()) { + // this should be a nt:file node + Node node = (Node) item; + if (!node.getPrimaryNodeType().isNodeType( + NodeType.NT_FILE)) + throw new IOException("Node " + node + " is not a " + + NodeType.NT_FILE); + + } else { + Property property = (Property) item; + if(property.getType()==PropertyType.BINARY){ + //Binary binary = property.getBinary(); + + } + } + } catch (RepositoryException e) { + IOException ioe = new IOException( + "Unexpected JCR exception"); + ioe.initCause(e); + throw ioe; + } + } + + @Override + public InputStream getInputStream() throws IOException { + // TODO Auto-generated method stub + return super.getInputStream(); + } + + }; + } + +} diff --git a/org.argeo.jcr/src/org/argeo/jcr/JcrUtils.java b/org.argeo.jcr/src/org/argeo/jcr/JcrUtils.java new file mode 100644 index 000000000..98a2da8f3 --- /dev/null +++ b/org.argeo.jcr/src/org/argeo/jcr/JcrUtils.java @@ -0,0 +1,1592 @@ +/* + * 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.jcr; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.net.MalformedURLException; +import java.net.URL; +import java.security.Principal; +import java.text.DateFormat; +import java.text.ParseException; +import java.util.ArrayList; +import java.util.Calendar; +import java.util.Collections; +import java.util.Date; +import java.util.GregorianCalendar; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.TreeMap; + +import javax.jcr.Binary; +import javax.jcr.NamespaceRegistry; +import javax.jcr.NoSuchWorkspaceException; +import javax.jcr.Node; +import javax.jcr.NodeIterator; +import javax.jcr.Property; +import javax.jcr.PropertyIterator; +import javax.jcr.PropertyType; +import javax.jcr.Repository; +import javax.jcr.RepositoryException; +import javax.jcr.Session; +import javax.jcr.Value; +import javax.jcr.Workspace; +import javax.jcr.nodetype.NodeType; +import javax.jcr.observation.EventListener; +import javax.jcr.query.Query; +import javax.jcr.query.QueryResult; +import javax.jcr.security.AccessControlEntry; +import javax.jcr.security.AccessControlList; +import javax.jcr.security.AccessControlManager; +import javax.jcr.security.AccessControlPolicy; +import javax.jcr.security.AccessControlPolicyIterator; +import javax.jcr.security.Privilege; + +import org.apache.commons.io.IOUtils; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.argeo.ArgeoMonitor; +import org.argeo.util.security.DigestUtils; +import org.argeo.util.security.SimplePrincipal; + +/** Utility methods to simplify common JCR operations. */ +public class JcrUtils implements ArgeoJcrConstants { + + final private static Log log = LogFactory.getLog(JcrUtils.class); + + /** + * Not complete yet. See + * http://www.day.com/specs/jcr/2.0/3_Repository_Model.html#3.2.2%20Local + * %20Names + */ + public final static char[] INVALID_NAME_CHARACTERS = { '/', ':', '[', ']', + '|', '*', /* + * invalid XML chars : + */ + '<', '>', '&' }; + + /** Prevents instantiation */ + private JcrUtils() { + } + + /** + * Queries one single node. + * + * @return one single node or null if none was found + * @throws ArgeoJcrException + * if more than one node was found + */ + public static Node querySingleNode(Query query) { + NodeIterator nodeIterator; + try { + QueryResult queryResult = query.execute(); + nodeIterator = queryResult.getNodes(); + } catch (RepositoryException e) { + throw new ArgeoJcrException("Cannot execute query " + query, e); + } + Node node; + if (nodeIterator.hasNext()) + node = nodeIterator.nextNode(); + else + return null; + + if (nodeIterator.hasNext()) + throw new ArgeoJcrException("Query returned more than one node."); + return node; + } + + /** Retrieves the node name from the provided path */ + public static String nodeNameFromPath(String path) { + if (path.equals("/")) + return ""; + if (path.charAt(0) != '/') + throw new ArgeoJcrException("Path " + path + " must start with a '/'"); + String pathT = path; + if (pathT.charAt(pathT.length() - 1) == '/') + pathT = pathT.substring(0, pathT.length() - 2); + + int index = pathT.lastIndexOf('/'); + return pathT.substring(index + 1); + } + + /** Retrieves the parent path of the provided path */ + public static String parentPath(String path) { + if (path.equals("/")) + throw new ArgeoJcrException("Root path '/' has no parent path"); + if (path.charAt(0) != '/') + throw new ArgeoJcrException("Path " + path + " must start with a '/'"); + String pathT = path; + if (pathT.charAt(pathT.length() - 1) == '/') + pathT = pathT.substring(0, pathT.length() - 2); + + int index = pathT.lastIndexOf('/'); + return pathT.substring(0, index); + } + + /** The provided data as a path ('/' at the end, not the beginning) */ + public static String dateAsPath(Calendar cal) { + return dateAsPath(cal, false); + } + + /** + * Creates a deep path based on a URL: + * http://subdomain.example.com/to/content?args => + * com/example/subdomain/to/content + */ + public static String urlAsPath(String url) { + try { + URL u = new URL(url); + StringBuffer path = new StringBuffer(url.length()); + // invert host + path.append(hostAsPath(u.getHost())); + // we don't put port since it may not always be there and may change + path.append(u.getPath()); + return path.toString(); + } catch (MalformedURLException e) { + throw new ArgeoJcrException("Cannot generate URL path for " + url, e); + } + } + + /** Set the {@link NodeType#NT_ADDRESS} properties based on this URL. */ + public static void urlToAddressProperties(Node node, String url) { + try { + URL u = new URL(url); + node.setProperty(Property.JCR_PROTOCOL, u.getProtocol()); + node.setProperty(Property.JCR_HOST, u.getHost()); + node.setProperty(Property.JCR_PORT, Integer.toString(u.getPort())); + node.setProperty(Property.JCR_PATH, normalizePath(u.getPath())); + } catch (Exception e) { + throw new ArgeoJcrException("Cannot set URL " + url + + " as nt:address properties", e); + } + } + + /** Build URL based on the {@link NodeType#NT_ADDRESS} properties. */ + public static String urlFromAddressProperties(Node node) { + try { + URL u = new URL( + node.getProperty(Property.JCR_PROTOCOL).getString(), node + .getProperty(Property.JCR_HOST).getString(), + (int) node.getProperty(Property.JCR_PORT).getLong(), node + .getProperty(Property.JCR_PATH).getString()); + return u.toString(); + } catch (Exception e) { + throw new ArgeoJcrException( + "Cannot get URL from nt:address properties of " + node, e); + } + } + + /* + * PATH UTILITIES + */ + + /** Make sure that: starts with '/', do not end with '/', do not have '//' */ + public static String normalizePath(String path) { + List tokens = tokenize(path); + StringBuffer buf = new StringBuffer(path.length()); + for (String token : tokens) { + buf.append('/'); + buf.append(token); + } + return buf.toString(); + } + + /** + * Creates a path from a FQDN, inverting the order of the component: + * www.argeo.org => org.argeo.www + */ + public static String hostAsPath(String host) { + StringBuffer path = new StringBuffer(host.length()); + String[] hostTokens = host.split("\\."); + for (int i = hostTokens.length - 1; i >= 0; i--) { + path.append(hostTokens[i]); + if (i != 0) + path.append('/'); + } + return path.toString(); + } + + /** + * Creates a path from a UUID (e.g. 6ebda899-217d-4bf1-abe4-2839085c8f3c => + * 6ebda899-217d/4bf1/abe4/2839085c8f3c/). '/' at the end, not the beginning + */ + public static String uuidAsPath(String uuid) { + StringBuffer path = new StringBuffer(uuid.length()); + String[] tokens = uuid.split("-"); + for (int i = 0; i < tokens.length; i++) { + path.append(tokens[i]); + if (i != 0) + path.append('/'); + } + return path.toString(); + } + + /** + * The provided data as a path ('/' at the end, not the beginning) + * + * @param cal + * the date + * @param addHour + * whether to add hour as well + */ + public static String dateAsPath(Calendar cal, Boolean addHour) { + StringBuffer buf = new StringBuffer(14); + buf.append('Y'); + buf.append(cal.get(Calendar.YEAR)); + buf.append('/'); + + int month = cal.get(Calendar.MONTH) + 1; + buf.append('M'); + if (month < 10) + buf.append(0); + buf.append(month); + buf.append('/'); + + int day = cal.get(Calendar.DAY_OF_MONTH); + buf.append('D'); + if (day < 10) + buf.append(0); + buf.append(day); + buf.append('/'); + + if (addHour) { + int hour = cal.get(Calendar.HOUR_OF_DAY); + buf.append('H'); + if (hour < 10) + buf.append(0); + buf.append(hour); + buf.append('/'); + } + return buf.toString(); + + } + + /** Converts in one call a string into a gregorian calendar. */ + public static Calendar parseCalendar(DateFormat dateFormat, String value) { + try { + Date date = dateFormat.parse(value); + Calendar calendar = new GregorianCalendar(); + calendar.setTime(date); + return calendar; + } catch (ParseException e) { + throw new ArgeoJcrException("Cannot parse " + value + + " with date format " + dateFormat, e); + } + + } + + /** The last element of a path. */ + public static String lastPathElement(String path) { + if (path.charAt(path.length() - 1) == '/') + throw new ArgeoJcrException("Path " + path + " cannot end with '/'"); + int index = path.lastIndexOf('/'); + if (index < 0) + return path; + return path.substring(index + 1); + } + + /** + * Call {@link Node#getName()} without exceptions (useful in super + * constructors). + */ + public static String getNameQuietly(Node node) { + try { + return node.getName(); + } catch (RepositoryException e) { + throw new ArgeoJcrException("Cannot get name from " + node, e); + } + } + + /** + * Call {@link Node#getProperty(String)} without exceptions (useful in super + * constructors). + */ + public static String getStringPropertyQuietly(Node node, String propertyName) { + try { + return node.getProperty(propertyName).getString(); + } catch (RepositoryException e) { + throw new ArgeoJcrException("Cannot get name from " + node, e); + } + } + + /** + * Routine that get the child with this name, adding id it does not already + * exist + */ + public static Node getOrAdd(Node parent, String childName, + String childPrimaryNodeType) throws RepositoryException { + return parent.hasNode(childName) ? parent.getNode(childName) : parent + .addNode(childName, childPrimaryNodeType); + } + + /** + * Routine that get the child with this name, adding id it does not already + * exist + */ + public static Node getOrAdd(Node parent, String childName) + throws RepositoryException { + return parent.hasNode(childName) ? parent.getNode(childName) : parent + .addNode(childName); + } + + /** Convert a {@link NodeIterator} to a list of {@link Node} */ + public static List nodeIteratorToList(NodeIterator nodeIterator) { + List nodes = new ArrayList(); + while (nodeIterator.hasNext()) { + nodes.add(nodeIterator.nextNode()); + } + return nodes; + } + + /* + * PROPERTIES + */ + + /** + * Concisely get the string value of a property or null if this node doesn't + * have this property + */ + public static String get(Node node, String propertyName) { + try { + if (!node.hasProperty(propertyName)) + return null; + return node.getProperty(propertyName).getString(); + } catch (RepositoryException e) { + throw new ArgeoJcrException("Cannot get property " + propertyName + + " of " + node, e); + } + } + + /** Concisely get the boolean value of a property */ + public static Boolean check(Node node, String propertyName) { + try { + return node.getProperty(propertyName).getBoolean(); + } catch (RepositoryException e) { + throw new ArgeoJcrException("Cannot get property " + propertyName + + " of " + node, e); + } + } + + /** Concisely get the bytes array value of a property */ + public static byte[] getBytes(Node node, String propertyName) { + try { + return getBinaryAsBytes(node.getProperty(propertyName)); + } catch (RepositoryException e) { + throw new ArgeoJcrException("Cannot get property " + propertyName + + " of " + node, e); + } + } + + /** Creates the nodes making path, if they don't exist. */ + public static Node mkdirs(Session session, String path) { + return mkdirs(session, path, null, null, false); + } + + /** + * use {@link #mkdirs(Session, String, String, String, Boolean)} instead. + * + * @deprecated + */ + @Deprecated + public static Node mkdirs(Session session, String path, String type, + Boolean versioning) { + return mkdirs(session, path, type, type, false); + } + + /** + * @param type + * the type of the leaf node + */ + public static Node mkdirs(Session session, String path, String type) { + return mkdirs(session, path, type, null, false); + } + + /** + * Create sub nodes relative to a parent node + * + * @param nodeType + * the type of the leaf node + */ + public static Node mkdirs(Node parentNode, String relativePath, + String nodeType) { + return mkdirs(parentNode, relativePath, nodeType, null); + } + + /** + * Create sub nodes relative to a parent node + * + * @param nodeType + * the type of the leaf node + */ + public static Node mkdirs(Node parentNode, String relativePath, + String nodeType, String intermediaryNodeType) { + List tokens = tokenize(relativePath); + Node currParent = parentNode; + try { + for (int i = 0; i < tokens.size(); i++) { + String name = tokens.get(i); + if (currParent.hasNode(name)) { + currParent = currParent.getNode(name); + } else { + if (i != (tokens.size() - 1)) {// intermediary + currParent = currParent.addNode(name, + intermediaryNodeType); + } else {// leaf + currParent = currParent.addNode(name, nodeType); + } + } + } + return currParent; + } catch (RepositoryException e) { + throw new ArgeoJcrException("Cannot mkdirs relative path " + + relativePath + " from " + parentNode, e); + } + } + + /** + * Synchronized and save is performed, to avoid race conditions in + * initializers leading to duplicate nodes. + */ + public synchronized static Node mkdirsSafe(Session session, String path, + String type) { + try { + if (session.hasPendingChanges()) + throw new ArgeoJcrException( + "Session has pending changes, save them first."); + Node node = mkdirs(session, path, type); + session.save(); + return node; + } catch (RepositoryException e) { + discardQuietly(session); + throw new ArgeoJcrException("Cannot safely make directories", e); + } + } + + public synchronized static Node mkdirsSafe(Session session, String path) { + return mkdirsSafe(session, path, null); + } + + /** + * Creates the nodes making path, if they don't exist. This is up to the + * caller to save the session. Use with caution since it can create + * duplicate nodes if used concurrently. + */ + public static Node mkdirs(Session session, String path, String type, + String intermediaryNodeType, Boolean versioning) { + try { + if (path.equals('/')) + return session.getRootNode(); + + if (session.itemExists(path)) { + Node node = session.getNode(path); + // check type + if (type != null && !node.isNodeType(type) + && !node.getPath().equals("/")) + throw new ArgeoJcrException("Node " + node + + " exists but is of type " + + node.getPrimaryNodeType().getName() + + " not of type " + type); + // TODO: check versioning + return node; + } + + StringBuffer current = new StringBuffer("/"); + Node currentNode = session.getRootNode(); + Iterator it = tokenize(path).iterator(); + while (it.hasNext()) { + String part = it.next(); + current.append(part).append('/'); + if (!session.itemExists(current.toString())) { + if (!it.hasNext() && type != null) + currentNode = currentNode.addNode(part, type); + else if (it.hasNext() && intermediaryNodeType != null) + currentNode = currentNode.addNode(part, + intermediaryNodeType); + else + currentNode = currentNode.addNode(part); + if (versioning) + currentNode.addMixin(NodeType.MIX_VERSIONABLE); + if (log.isTraceEnabled()) + log.debug("Added folder " + part + " as " + current); + } else { + currentNode = (Node) session.getItem(current.toString()); + } + } + return currentNode; + } catch (RepositoryException e) { + discardQuietly(session); + throw new ArgeoJcrException("Cannot mkdirs " + path, e); + } finally { + } + } + + /** Convert a path to the list of its tokens */ + public static List tokenize(String path) { + List tokens = new ArrayList(); + boolean optimized = false; + if (!optimized) { + String[] rawTokens = path.split("/"); + for (String token : rawTokens) { + if (!token.equals("")) + tokens.add(token); + } + } else { + StringBuffer curr = new StringBuffer(); + char[] arr = path.toCharArray(); + chars: for (int i = 0; i < arr.length; i++) { + char c = arr[i]; + if (c == '/') { + if (i == 0 || (i == arr.length - 1)) + continue chars; + if (curr.length() > 0) { + tokens.add(curr.toString()); + curr = new StringBuffer(); + } + } else + curr.append(c); + } + if (curr.length() > 0) { + tokens.add(curr.toString()); + curr = new StringBuffer(); + } + } + return Collections.unmodifiableList(tokens); + } + + /** + * Safe and repository implementation independent registration of a + * namespace. + */ + public static void registerNamespaceSafely(Session session, String prefix, + String uri) { + try { + registerNamespaceSafely(session.getWorkspace() + .getNamespaceRegistry(), prefix, uri); + } catch (RepositoryException e) { + throw new ArgeoJcrException("Cannot find namespace registry", e); + } + } + + /** + * Safe and repository implementation independent registration of a + * namespace. + */ + public static void registerNamespaceSafely(NamespaceRegistry nr, + String prefix, String uri) { + try { + String[] prefixes = nr.getPrefixes(); + for (String pref : prefixes) + if (pref.equals(prefix)) { + String registeredUri = nr.getURI(pref); + if (!registeredUri.equals(uri)) + throw new ArgeoJcrException("Prefix " + pref + + " already registered for URI " + + registeredUri + + " which is different from provided URI " + + uri); + else + return;// skip + } + nr.registerNamespace(prefix, uri); + } catch (RepositoryException e) { + throw new ArgeoJcrException("Cannot register namespace " + uri + + " under prefix " + prefix, e); + } + } + + /** Recursively outputs the contents of the given node. */ + public static void debug(Node node) { + debug(node, log); + } + + /** Recursively outputs the contents of the given node. */ + public static void debug(Node node, Log log) { + try { + // First output the node path + log.debug(node.getPath()); + // Skip the virtual (and large!) jcr:system subtree + if (node.getName().equals("jcr:system")) { + return; + } + + // Then the children nodes (recursive) + NodeIterator it = node.getNodes(); + while (it.hasNext()) { + Node childNode = it.nextNode(); + debug(childNode, log); + } + + // Then output the properties + PropertyIterator properties = node.getProperties(); + // log.debug("Property are : "); + + properties: while (properties.hasNext()) { + Property property = properties.nextProperty(); + if (property.getType() == PropertyType.BINARY) + continue properties;// skip + if (property.getDefinition().isMultiple()) { + // A multi-valued property, print all values + Value[] values = property.getValues(); + for (int i = 0; i < values.length; i++) { + log.debug(property.getPath() + "=" + + values[i].getString()); + } + } else { + // A single-valued property + log.debug(property.getPath() + "=" + property.getString()); + } + } + } catch (Exception e) { + log.error("Could not debug " + node, e); + } + + } + + /** Logs the effective access control policies */ + public static void logEffectiveAccessPolicies(Node node) { + try { + logEffectiveAccessPolicies(node.getSession(), node.getPath()); + } catch (RepositoryException e) { + log.error("Cannot log effective access policies of " + node, e); + } + } + + /** Logs the effective access control policies */ + public static void logEffectiveAccessPolicies(Session session, String path) { + if (!log.isDebugEnabled()) + return; + + try { + AccessControlPolicy[] effectivePolicies = session + .getAccessControlManager().getEffectivePolicies(path); + if (effectivePolicies.length > 0) { + for (AccessControlPolicy policy : effectivePolicies) { + if (policy instanceof AccessControlList) { + AccessControlList acl = (AccessControlList) policy; + log.debug("Access control list for " + path + "\n" + + accessControlListSummary(acl)); + } + } + } else { + log.debug("No effective access control policy for " + path); + } + } catch (RepositoryException e) { + log.error("Cannot log effective access policies of " + path, e); + } + } + + /** Returns a human-readable summary of this access control list. */ + public static String accessControlListSummary(AccessControlList acl) { + StringBuffer buf = new StringBuffer(""); + try { + for (AccessControlEntry ace : acl.getAccessControlEntries()) { + buf.append('\t').append(ace.getPrincipal().getName()) + .append('\n'); + for (Privilege priv : ace.getPrivileges()) + buf.append("\t\t").append(priv.getName()).append('\n'); + } + return buf.toString(); + } catch (RepositoryException e) { + throw new ArgeoJcrException("Cannot write summary of " + acl, e); + } + } + + /** + * Copies recursively the content of a node to another one. Do NOT copy the + * property values of {@link NodeType#MIX_CREATED} and + * {@link NodeType#MIX_LAST_MODIFIED}, but update the + * {@link Property#JCR_LAST_MODIFIED} and + * {@link Property#JCR_LAST_MODIFIED_BY} properties if the target node has + * the {@link NodeType#MIX_LAST_MODIFIED} mixin. + */ + public static void copy(Node fromNode, Node toNode) { + try { + if (toNode.getDefinition().isProtected()) + return; + + // process properties + PropertyIterator pit = fromNode.getProperties(); + properties: while (pit.hasNext()) { + Property fromProperty = pit.nextProperty(); + String propertyName = fromProperty.getName(); + if (toNode.hasProperty(propertyName) + && toNode.getProperty(propertyName).getDefinition() + .isProtected()) + continue properties; + + if (fromProperty.getDefinition().isProtected()) + continue properties; + + if (propertyName.equals("jcr:created") + || propertyName.equals("jcr:createdBy") + || propertyName.equals("jcr:lastModified") + || propertyName.equals("jcr:lastModifiedBy")) + continue properties; + + if (fromProperty.isMultiple()) { + toNode.setProperty(propertyName, fromProperty.getValues()); + } else { + toNode.setProperty(propertyName, fromProperty.getValue()); + } + } + + // update jcr:lastModified and jcr:lastModifiedBy in toNode in case + // they existed, before adding the mixins + updateLastModified(toNode); + + // add mixins + for (NodeType mixinType : fromNode.getMixinNodeTypes()) { + toNode.addMixin(mixinType.getName()); + } + + // process children nodes + NodeIterator nit = fromNode.getNodes(); + while (nit.hasNext()) { + Node fromChild = nit.nextNode(); + Integer index = fromChild.getIndex(); + String nodeRelPath = fromChild.getName() + "[" + index + "]"; + Node toChild; + if (toNode.hasNode(nodeRelPath)) + toChild = toNode.getNode(nodeRelPath); + else + toChild = toNode.addNode(fromChild.getName(), fromChild + .getPrimaryNodeType().getName()); + copy(fromChild, toChild); + } + } catch (RepositoryException e) { + throw new ArgeoJcrException("Cannot copy " + fromNode + " to " + + toNode, e); + } + } + + /** + * Check whether all first-level properties (except jcr:* properties) are + * equal. Skip jcr:* properties + */ + public static Boolean allPropertiesEquals(Node reference, Node observed, + Boolean onlyCommonProperties) { + try { + PropertyIterator pit = reference.getProperties(); + props: while (pit.hasNext()) { + Property propReference = pit.nextProperty(); + String propName = propReference.getName(); + if (propName.startsWith("jcr:")) + continue props; + + if (!observed.hasProperty(propName)) + if (onlyCommonProperties) + continue props; + else + return false; + // TODO: deal with multiple property values? + if (!observed.getProperty(propName).getValue() + .equals(propReference.getValue())) + return false; + } + return true; + } catch (RepositoryException e) { + throw new ArgeoJcrException("Cannot check all properties equals of " + + reference + " and " + observed, e); + } + } + + public static Map diffProperties(Node reference, + Node observed) { + Map diffs = new TreeMap(); + diffPropertiesLevel(diffs, null, reference, observed); + return diffs; + } + + /** + * Compare the properties of two nodes. Recursivity to child nodes is not + * yet supported. Skip jcr:* properties. + */ + static void diffPropertiesLevel(Map diffs, + String baseRelPath, Node reference, Node observed) { + try { + // check removed and modified + PropertyIterator pit = reference.getProperties(); + props: while (pit.hasNext()) { + Property p = pit.nextProperty(); + String name = p.getName(); + if (name.startsWith("jcr:")) + continue props; + + if (!observed.hasProperty(name)) { + String relPath = propertyRelPath(baseRelPath, name); + PropertyDiff pDiff = new PropertyDiff(PropertyDiff.REMOVED, + relPath, p.getValue(), null); + diffs.put(relPath, pDiff); + } else { + if (p.isMultiple()) { + // FIXME implement multiple + } else { + Value referenceValue = p.getValue(); + Value newValue = observed.getProperty(name).getValue(); + if (!referenceValue.equals(newValue)) { + String relPath = propertyRelPath(baseRelPath, name); + PropertyDiff pDiff = new PropertyDiff( + PropertyDiff.MODIFIED, relPath, + referenceValue, newValue); + diffs.put(relPath, pDiff); + } + } + } + } + // check added + pit = observed.getProperties(); + props: while (pit.hasNext()) { + Property p = pit.nextProperty(); + String name = p.getName(); + if (name.startsWith("jcr:")) + continue props; + if (!reference.hasProperty(name)) { + if (p.isMultiple()) { + // FIXME implement multiple + } else { + String relPath = propertyRelPath(baseRelPath, name); + PropertyDiff pDiff = new PropertyDiff( + PropertyDiff.ADDED, relPath, null, p.getValue()); + diffs.put(relPath, pDiff); + } + } + } + } catch (RepositoryException e) { + throw new ArgeoJcrException("Cannot diff " + reference + " and " + + observed, e); + } + } + + /** + * Compare only a restricted list of properties of two nodes. No + * recursivity. + * + */ + public static Map diffProperties(Node reference, + Node observed, List properties) { + Map diffs = new TreeMap(); + try { + Iterator pit = properties.iterator(); + + props: while (pit.hasNext()) { + String name = pit.next(); + if (!reference.hasProperty(name)) { + if (!observed.hasProperty(name)) + continue props; + Value val = observed.getProperty(name).getValue(); + try { + // empty String but not null + if ("".equals(val.getString())) + continue props; + } catch (Exception e) { + // not parseable as String, silent + } + PropertyDiff pDiff = new PropertyDiff(PropertyDiff.ADDED, + name, null, val); + diffs.put(name, pDiff); + } else if (!observed.hasProperty(name)) { + PropertyDiff pDiff = new PropertyDiff(PropertyDiff.REMOVED, + name, reference.getProperty(name).getValue(), null); + diffs.put(name, pDiff); + } else { + Value referenceValue = reference.getProperty(name) + .getValue(); + Value newValue = observed.getProperty(name).getValue(); + if (!referenceValue.equals(newValue)) { + PropertyDiff pDiff = new PropertyDiff( + PropertyDiff.MODIFIED, name, referenceValue, + newValue); + diffs.put(name, pDiff); + } + } + } + } catch (RepositoryException e) { + throw new ArgeoJcrException("Cannot diff " + reference + " and " + + observed, e); + } + return diffs; + } + + /** Builds a property relPath to be used in the diff. */ + private static String propertyRelPath(String baseRelPath, + String propertyName) { + if (baseRelPath == null) + return propertyName; + else + return baseRelPath + '/' + propertyName; + } + + /** + * Normalizes a name so that it can be stored in contexts not supporting + * names with ':' (typically databases). Replaces ':' by '_'. + */ + public static String normalize(String name) { + return name.replace(':', '_'); + } + + /** + * Replaces characters which are invalid in a JCR name by '_'. Currently not + * exhaustive. + * + * @see JcrUtils#INVALID_NAME_CHARACTERS + */ + public static String replaceInvalidChars(String name) { + return replaceInvalidChars(name, '_'); + } + + /** + * Replaces characters which are invalid in a JCR name. Currently not + * exhaustive. + * + * @see JcrUtils#INVALID_NAME_CHARACTERS + */ + public static String replaceInvalidChars(String name, char replacement) { + boolean modified = false; + char[] arr = name.toCharArray(); + for (int i = 0; i < arr.length; i++) { + char c = arr[i]; + invalid: for (char invalid : INVALID_NAME_CHARACTERS) { + if (c == invalid) { + arr[i] = replacement; + modified = true; + break invalid; + } + } + } + if (modified) + return new String(arr); + else + // do not create new object if unnecessary + return name; + } + + /** + * Removes forbidden characters from a path, replacing them with '_' + * + * @deprecated use {@link #replaceInvalidChars(String)} instead + */ + public static String removeForbiddenCharacters(String str) { + return str.replace('[', '_').replace(']', '_').replace('/', '_') + .replace('*', '_'); + + } + + /** Cleanly disposes a {@link Binary} even if it is null. */ + public static void closeQuietly(Binary binary) { + if (binary == null) + return; + binary.dispose(); + } + + /** Retrieve a {@link Binary} as a byte array */ + public static byte[] getBinaryAsBytes(Property property) { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + InputStream in = null; + Binary binary = null; + try { + binary = property.getBinary(); + in = binary.getStream(); + IOUtils.copy(in, out); + return out.toByteArray(); + } catch (Exception e) { + throw new ArgeoJcrException("Cannot read binary " + property + + " as bytes", e); + } finally { + IOUtils.closeQuietly(out); + IOUtils.closeQuietly(in); + closeQuietly(binary); + } + } + + /** Writes a {@link Binary} from a byte array */ + public static void setBinaryAsBytes(Node node, String property, byte[] bytes) { + InputStream in = null; + Binary binary = null; + try { + in = new ByteArrayInputStream(bytes); + binary = node.getSession().getValueFactory().createBinary(in); + node.setProperty(property, binary); + } catch (Exception e) { + throw new ArgeoJcrException("Cannot read binary " + property + + " as bytes", e); + } finally { + IOUtils.closeQuietly(in); + closeQuietly(binary); + } + } + + /** + * Creates depth from a string (typically a username) by adding levels based + * on its first characters: "aBcD",2 => a/aB + */ + public static String firstCharsToPath(String str, Integer nbrOfChars) { + if (str.length() < nbrOfChars) + throw new ArgeoJcrException("String " + str + + " length must be greater or equal than " + nbrOfChars); + StringBuffer path = new StringBuffer(""); + StringBuffer curr = new StringBuffer(""); + for (int i = 0; i < nbrOfChars; i++) { + curr.append(str.charAt(i)); + path.append(curr); + if (i < nbrOfChars - 1) + path.append('/'); + } + return path.toString(); + } + + /** + * Discards the current changes in the session attached to this node. To be + * used typically in a catch block. + * + * @see #discardQuietly(Session) + */ + public static void discardUnderlyingSessionQuietly(Node node) { + try { + discardQuietly(node.getSession()); + } catch (RepositoryException e) { + log.warn("Cannot quietly discard session of node " + node + ": " + + e.getMessage()); + } + } + + /** + * Discards the current changes in a session by calling + * {@link Session#refresh(boolean)} with false, only logging + * potential errors when doing so. To be used typically in a catch block. + */ + public static void discardQuietly(Session session) { + try { + if (session != null) + session.refresh(false); + } catch (RepositoryException e) { + log.warn("Cannot quietly discard session " + session + ": " + + e.getMessage()); + } + } + + /** + * Login to a workspace with implicit credentials, creates the workspace + * with these credentials if it does not already exist. + */ + public static Session loginOrCreateWorkspace(Repository repository, + String workspaceName) throws RepositoryException { + Session workspaceSession = null; + Session defaultSession = null; + try { + try { + workspaceSession = repository.login(workspaceName); + } catch (NoSuchWorkspaceException e) { + // try to create workspace + defaultSession = repository.login(); + defaultSession.getWorkspace().createWorkspace(workspaceName); + workspaceSession = repository.login(workspaceName); + } + return workspaceSession; + } finally { + logoutQuietly(defaultSession); + } + } + + /** Logs out the session, not throwing any exception, even if it is null. */ + public static void logoutQuietly(Session session) { + try { + if (session != null) + if (session.isLive()) + session.logout(); + } catch (Exception e) { + // silent + } + } + + /** + * Convenient method to add a listener. uuids passed as null, deep=true, + * local=true, only one node type + */ + public static void addListener(Session session, EventListener listener, + int eventTypes, String basePath, String nodeType) { + try { + session.getWorkspace() + .getObservationManager() + .addEventListener( + listener, + eventTypes, + basePath, + true, + null, + nodeType == null ? null : new String[] { nodeType }, + true); + } catch (RepositoryException e) { + throw new ArgeoJcrException("Cannot add JCR listener " + listener + + " to session " + session, e); + } + } + + /** Removes a listener without throwing exception */ + public static void removeListenerQuietly(Session session, + EventListener listener) { + if (session == null || !session.isLive()) + return; + try { + session.getWorkspace().getObservationManager() + .removeEventListener(listener); + } catch (RepositoryException e) { + // silent + } + } + + /** + * Quietly unregisters an {@link EventListener} from the udnerlying + * workspace of this node. + */ + public static void unregisterQuietly(Node node, EventListener eventListener) { + try { + unregisterQuietly(node.getSession().getWorkspace(), eventListener); + } catch (RepositoryException e) { + // silent + if (log.isTraceEnabled()) + log.trace("Could not unregister event listener " + + eventListener); + } + } + + /** Quietly unregisters an {@link EventListener} from this workspace */ + public static void unregisterQuietly(Workspace workspace, + EventListener eventListener) { + if (eventListener == null) + return; + try { + workspace.getObservationManager() + .removeEventListener(eventListener); + } catch (RepositoryException e) { + // silent + if (log.isTraceEnabled()) + log.trace("Could not unregister event listener " + + eventListener); + } + } + + /** + * If this node is has the {@link NodeType#MIX_LAST_MODIFIED} mixin, it + * updates the {@link Property#JCR_LAST_MODIFIED} property with the current + * time and the {@link Property#JCR_LAST_MODIFIED_BY} property with the + * underlying session user id. In Jackrabbit 2.x, these properties + * are not automatically updated, hence the need for manual update. The + * session is not saved. + */ + public static void updateLastModified(Node node) { + try { + if (!node.isNodeType(NodeType.MIX_LAST_MODIFIED)) + node.addMixin(NodeType.MIX_LAST_MODIFIED); + node.setProperty(Property.JCR_LAST_MODIFIED, + new GregorianCalendar()); + node.setProperty(Property.JCR_LAST_MODIFIED_BY, node.getSession() + .getUserID()); + } catch (RepositoryException e) { + throw new ArgeoJcrException("Cannot update last modified on " + node, + e); + } + } + + /** + * Update lastModified recursively until this parent. + * + * @param node + * the node + * @param untilPath + * the base path, null is equivalent to "/" + */ + public static void updateLastModifiedAndParents(Node node, String untilPath) { + try { + if (untilPath != null && !node.getPath().startsWith(untilPath)) + throw new ArgeoJcrException(node + " is not under " + untilPath); + updateLastModified(node); + if (untilPath == null) { + if (!node.getPath().equals("/")) + updateLastModifiedAndParents(node.getParent(), untilPath); + } else { + if (!node.getPath().equals(untilPath)) + updateLastModifiedAndParents(node.getParent(), untilPath); + } + } catch (RepositoryException e) { + throw new ArgeoJcrException("Cannot update lastModified from " + node + + " until " + untilPath, e); + } + } + + /** + * Returns a String representing the short version (see Node type + * Notation attributes grammar) of the main business attributes of this + * property definition + * + * @param prop + */ + public static String getPropertyDefinitionAsString(Property prop) { + StringBuffer sbuf = new StringBuffer(); + try { + if (prop.getDefinition().isAutoCreated()) + sbuf.append("a"); + if (prop.getDefinition().isMandatory()) + sbuf.append("m"); + if (prop.getDefinition().isProtected()) + sbuf.append("p"); + if (prop.getDefinition().isMultiple()) + sbuf.append("*"); + } catch (RepositoryException re) { + throw new ArgeoJcrException( + "unexpected error while getting property definition as String", + re); + } + return sbuf.toString(); + } + + /** + * Estimate the sub tree size from current node. Computation is based on the + * Jcr {@link Property.getLength()} method. Note : it is not the exact size + * used on the disk by the current part of the JCR Tree. + */ + + public static long getNodeApproxSize(Node node) { + long curNodeSize = 0; + try { + PropertyIterator pi = node.getProperties(); + while (pi.hasNext()) { + Property prop = pi.nextProperty(); + if (prop.isMultiple()) { + int nb = prop.getLengths().length; + for (int i = 0; i < nb; i++) { + curNodeSize += (prop.getLengths()[i] > 0 ? prop + .getLengths()[i] : 0); + } + } else + curNodeSize += (prop.getLength() > 0 ? prop.getLength() : 0); + } + + NodeIterator ni = node.getNodes(); + while (ni.hasNext()) + curNodeSize += getNodeApproxSize(ni.nextNode()); + return curNodeSize; + } catch (RepositoryException re) { + throw new ArgeoJcrException( + "Unexpected error while recursively determining node size.", + re); + } + } + + /* + * SECURITY + */ + + /** + * Convenience method for adding a single privilege to a principal (user or + * role), typically jcr:all + */ + public synchronized static void addPrivilege(Session session, String path, + String principal, String privilege) throws RepositoryException { + List privileges = new ArrayList(); + privileges.add(session.getAccessControlManager().privilegeFromName( + privilege)); + addPrivileges(session, path, new SimplePrincipal(principal), privileges); + } + + /** + * Add privileges on a path to a {@link Principal}. The path must already + * exist. Session is saved. Synchronized to prevent concurrent modifications + * of the same node. + */ + public synchronized static Boolean addPrivileges(Session session, + String path, Principal principal, List privs) + throws RepositoryException { + // make sure the session is in line with the persisted state + session.refresh(false); + AccessControlManager acm = session.getAccessControlManager(); + AccessControlList acl = getAccessControlList(acm, path); + + accessControlEntries: for (AccessControlEntry ace : acl + .getAccessControlEntries()) { + Principal currentPrincipal = ace.getPrincipal(); + if (currentPrincipal.getName().equals(principal.getName())) { + Privilege[] currentPrivileges = ace.getPrivileges(); + if (currentPrivileges.length != privs.size()) + break accessControlEntries; + for (int i = 0; i < currentPrivileges.length; i++) { + Privilege currP = currentPrivileges[i]; + Privilege p = privs.get(i); + if (!currP.getName().equals(p.getName())) { + break accessControlEntries; + } + } + return false; + } + } + + Privilege[] privileges = privs.toArray(new Privilege[privs.size()]); + acl.addAccessControlEntry(principal, privileges); + acm.setPolicy(path, acl); + if (log.isDebugEnabled()) { + StringBuffer privBuf = new StringBuffer(); + for (Privilege priv : privs) + privBuf.append(priv.getName()); + log.debug("Added privileges " + privBuf + " to " + + principal.getName() + " on " + path + " in '" + + session.getWorkspace().getName() + "'"); + } + session.refresh(true); + session.save(); + return true; + } + + /** Gets access control list for this path, throws exception if not found */ + public synchronized static AccessControlList getAccessControlList( + AccessControlManager acm, String path) throws RepositoryException { + // search for an access control list + AccessControlList acl = null; + AccessControlPolicyIterator policyIterator = acm + .getApplicablePolicies(path); + if (policyIterator.hasNext()) { + while (policyIterator.hasNext()) { + AccessControlPolicy acp = policyIterator + .nextAccessControlPolicy(); + if (acp instanceof AccessControlList) + acl = ((AccessControlList) acp); + } + } else { + AccessControlPolicy[] existingPolicies = acm.getPolicies(path); + for (AccessControlPolicy acp : existingPolicies) { + if (acp instanceof AccessControlList) + acl = ((AccessControlList) acp); + } + } + if (acl != null) + return acl; + else + throw new ArgeoJcrException("ACL not found at " + path); + } + + /** Clear authorizations for a user at this path */ + public synchronized static void clearAccessControList(Session session, + String path, String username) throws RepositoryException { + AccessControlManager acm = session.getAccessControlManager(); + AccessControlList acl = getAccessControlList(acm, path); + for (AccessControlEntry ace : acl.getAccessControlEntries()) { + if (ace.getPrincipal().getName().equals(username)) { + acl.removeAccessControlEntry(ace); + } + } + // the new access control list must be applied otherwise this call: + // acl.removeAccessControlEntry(ace); has no effect + acm.setPolicy(path, acl); + } + + /* + * FILES UTILITIES + */ + /** + * Creates the nodes making the path as {@link NodeType#NT_FOLDER} + */ + public static Node mkfolders(Session session, String path) { + return mkdirs(session, path, NodeType.NT_FOLDER, NodeType.NT_FOLDER, + false); + } + + /** + * Copy only nt:folder and nt:file, without their additional types and + * properties. + * + * @param recursive + * if true copies folders as well, otherwise only first level + * files + * @return how many files were copied + */ + @SuppressWarnings("deprecation") + public static Long copyFiles(Node fromNode, Node toNode, Boolean recursive, + ArgeoMonitor monitor) { + long count = 0l; + + Binary binary = null; + InputStream in = null; + try { + NodeIterator fromChildren = fromNode.getNodes(); + while (fromChildren.hasNext()) { + if (monitor != null && monitor.isCanceled()) + throw new ArgeoJcrException( + "Copy cancelled before it was completed"); + + Node fromChild = fromChildren.nextNode(); + String fileName = fromChild.getName(); + if (fromChild.isNodeType(NodeType.NT_FILE)) { + if (monitor != null) + monitor.subTask("Copy " + fileName); + binary = fromChild.getNode(Node.JCR_CONTENT) + .getProperty(Property.JCR_DATA).getBinary(); + in = binary.getStream(); + copyStreamAsFile(toNode, fileName, in); + IOUtils.closeQuietly(in); + closeQuietly(binary); + + // save session + toNode.getSession().save(); + count++; + + if (log.isDebugEnabled()) + log.debug("Copied file " + fromChild.getPath()); + if (monitor != null) + monitor.worked(1); + } else if (fromChild.isNodeType(NodeType.NT_FOLDER) + && recursive) { + Node toChildFolder; + if (toNode.hasNode(fileName)) { + toChildFolder = toNode.getNode(fileName); + if (!toChildFolder.isNodeType(NodeType.NT_FOLDER)) + throw new ArgeoJcrException(toChildFolder + + " is not of type nt:folder"); + } else { + toChildFolder = toNode.addNode(fileName, + NodeType.NT_FOLDER); + + // save session + toNode.getSession().save(); + } + count = count + + copyFiles(fromChild, toChildFolder, recursive, + monitor); + } + } + return count; + } catch (RepositoryException e) { + throw new ArgeoJcrException("Cannot copy files between " + fromNode + + " and " + toNode); + } finally { + // in case there was an exception + IOUtils.closeQuietly(in); + closeQuietly(binary); + } + } + + /** + * Iteratively count all file nodes in subtree, inefficient but can be + * useful when query are poorly supported, such as in remoting. + */ + public static Long countFiles(Node node) { + Long localCount = 0l; + try { + for (NodeIterator nit = node.getNodes(); nit.hasNext();) { + Node child = nit.nextNode(); + if (child.isNodeType(NodeType.NT_FOLDER)) + localCount = localCount + countFiles(child); + else if (child.isNodeType(NodeType.NT_FILE)) + localCount = localCount + 1; + } + } catch (RepositoryException e) { + throw new ArgeoJcrException("Cannot count all children of " + node); + } + return localCount; + } + + /** + * Copy a file as an nt:file, assuming an nt:folder hierarchy. The session + * is NOT saved. + * + * @return the created file node + */ + public static Node copyFile(Node folderNode, File file) { + InputStream in = null; + try { + in = new FileInputStream(file); + return copyStreamAsFile(folderNode, file.getName(), in); + } catch (IOException e) { + throw new ArgeoJcrException("Cannot copy file " + file + " under " + + folderNode, e); + } finally { + IOUtils.closeQuietly(in); + } + } + + /** Copy bytes as an nt:file */ + public static Node copyBytesAsFile(Node folderNode, String fileName, + byte[] bytes) { + InputStream in = null; + try { + in = new ByteArrayInputStream(bytes); + return copyStreamAsFile(folderNode, fileName, in); + } catch (Exception e) { + throw new ArgeoJcrException("Cannot copy file " + fileName + " under " + + folderNode, e); + } finally { + IOUtils.closeQuietly(in); + } + } + + /** + * Copy a stream as an nt:file, assuming an nt:folder hierarchy. The session + * is NOT saved. + * + * @return the created file node + */ + public static Node copyStreamAsFile(Node folderNode, String fileName, + InputStream in) { + Binary binary = null; + try { + Node fileNode; + Node contentNode; + if (folderNode.hasNode(fileName)) { + fileNode = folderNode.getNode(fileName); + if (!fileNode.isNodeType(NodeType.NT_FILE)) + throw new ArgeoJcrException(fileNode + + " is not of type nt:file"); + // we assume that the content node is already there + contentNode = fileNode.getNode(Node.JCR_CONTENT); + } else { + fileNode = folderNode.addNode(fileName, NodeType.NT_FILE); + contentNode = fileNode.addNode(Node.JCR_CONTENT, + NodeType.NT_RESOURCE); + } + binary = contentNode.getSession().getValueFactory() + .createBinary(in); + contentNode.setProperty(Property.JCR_DATA, binary); + return fileNode; + } catch (Exception e) { + throw new ArgeoJcrException("Cannot create file node " + fileName + + " under " + folderNode, e); + } finally { + closeQuietly(binary); + } + } + + /** Computes the checksum of an nt:file */ + public static String checksumFile(Node fileNode, String algorithm) { + Binary data = null; + InputStream in = null; + try { + data = fileNode.getNode(Node.JCR_CONTENT) + .getProperty(Property.JCR_DATA).getBinary(); + in = data.getStream(); + return DigestUtils.digest(algorithm, in); + } catch (RepositoryException e) { + throw new ArgeoJcrException("Cannot checksum file " + fileNode, e); + } finally { + IOUtils.closeQuietly(in); + closeQuietly(data); + } + } + +} diff --git a/org.argeo.jcr/src/org/argeo/jcr/PropertyDiff.java b/org.argeo.jcr/src/org/argeo/jcr/PropertyDiff.java new file mode 100644 index 000000000..a0ff4712a --- /dev/null +++ b/org.argeo.jcr/src/org/argeo/jcr/PropertyDiff.java @@ -0,0 +1,76 @@ +/* + * 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.jcr; + +import javax.jcr.Value; + +/** The result of the comparison of two JCR properties. */ +public class PropertyDiff { + public final static Integer MODIFIED = 0; + public final static Integer ADDED = 1; + public final static Integer REMOVED = 2; + + private final Integer type; + private final String relPath; + private final Value referenceValue; + private final Value newValue; + + public PropertyDiff(Integer type, String relPath, Value referenceValue, + Value newValue) { + super(); + + if (type == MODIFIED) { + if (referenceValue == null || newValue == null) + throw new ArgeoJcrException( + "Reference and new values must be specified."); + } else if (type == ADDED) { + if (referenceValue != null || newValue == null) + throw new ArgeoJcrException( + "New value and only it must be specified."); + } else if (type == REMOVED) { + if (referenceValue == null || newValue != null) + throw new ArgeoJcrException( + "Reference value and only it must be specified."); + } else { + throw new ArgeoJcrException("Unkown diff type " + type); + } + + if (relPath == null) + throw new ArgeoJcrException("Relative path must be specified"); + + this.type = type; + this.relPath = relPath; + this.referenceValue = referenceValue; + this.newValue = newValue; + } + + public Integer getType() { + return type; + } + + public String getRelPath() { + return relPath; + } + + public Value getReferenceValue() { + return referenceValue; + } + + public Value getNewValue() { + return newValue; + } + +} diff --git a/org.argeo.jcr/src/org/argeo/jcr/RepositoryRegister.java b/org.argeo.jcr/src/org/argeo/jcr/RepositoryRegister.java new file mode 100644 index 000000000..2e3d4550d --- /dev/null +++ b/org.argeo.jcr/src/org/argeo/jcr/RepositoryRegister.java @@ -0,0 +1,31 @@ +/* + * 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.jcr; + +import java.util.Map; + +import javax.jcr.Repository; +import javax.jcr.RepositoryFactory; + +/** Allows to register repositories by name. */ +public interface RepositoryRegister extends RepositoryFactory { + /** + * The registered {@link Repository} as a read-only map. Note that this + * method should be called for each access in order to be sure to be up to + * date in case repositories have registered/unregistered + */ + public Map getRepositories(); +} diff --git a/org.argeo.jcr/src/org/argeo/jcr/ThreadBoundJcrSessionFactory.java b/org.argeo.jcr/src/org/argeo/jcr/ThreadBoundJcrSessionFactory.java new file mode 100644 index 000000000..281bd015c --- /dev/null +++ b/org.argeo.jcr/src/org/argeo/jcr/ThreadBoundJcrSessionFactory.java @@ -0,0 +1,295 @@ +/* + * 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.jcr; + +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +import javax.jcr.LoginException; +import javax.jcr.Repository; +import javax.jcr.RepositoryException; +import javax.jcr.Session; +import javax.jcr.SimpleCredentials; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** Proxy JCR sessions and attach them to calling threads. */ +@Deprecated +public abstract class ThreadBoundJcrSessionFactory { + private final static Log log = LogFactory.getLog(ThreadBoundJcrSessionFactory.class); + + private Repository repository; + /** can be injected as list, only used if repository is null */ + private List repositories; + + private ThreadLocal session = new ThreadLocal(); + private final Session proxiedSession; + /** If workspace is null, default will be used. */ + private String workspace = null; + + private String defaultUsername = "demo"; + private String defaultPassword = "demo"; + private Boolean forceDefaultCredentials = false; + + private boolean active = true; + + // monitoring + private final List threads = Collections.synchronizedList(new ArrayList()); + private final Map activeSessions = Collections.synchronizedMap(new HashMap()); + private MonitoringThread monitoringThread; + + public ThreadBoundJcrSessionFactory() { + Class[] interfaces = { Session.class }; + proxiedSession = (Session) Proxy.newProxyInstance(ThreadBoundJcrSessionFactory.class.getClassLoader(), + interfaces, new JcrSessionInvocationHandler()); + } + + /** Logs in to the repository using various strategies. */ + protected synchronized Session login() { + if (!isActive()) + throw new ArgeoJcrException("Thread bound session factory inactive"); + + // discard session previously attached to this thread + Thread thread = Thread.currentThread(); + if (activeSessions.containsKey(thread.getId())) { + Session oldSession = activeSessions.remove(thread.getId()); + oldSession.logout(); + session.remove(); + } + + Session newSession = null; + // first try to login without credentials, assuming the underlying login + // module will have dealt with authentication (typically using Spring + // Security) + if (!forceDefaultCredentials) + try { + newSession = repository().login(workspace); + } catch (LoginException e1) { + log.warn("Cannot login without credentials: " + e1.getMessage()); + // invalid credentials, go to the next step + } catch (RepositoryException e1) { + // other kind of exception, fail + throw new ArgeoJcrException("Cannot log in to repository", e1); + } + + // log using default username / password (useful for testing purposes) + if (newSession == null) + try { + SimpleCredentials sc = new SimpleCredentials(defaultUsername, defaultPassword.toCharArray()); + newSession = repository().login(sc, workspace); + } catch (RepositoryException e) { + throw new ArgeoJcrException("Cannot log in to repository", e); + } + + session.set(newSession); + // Log and monitor new session + if (log.isTraceEnabled()) + log.trace("Logged in to JCR session " + newSession + "; userId=" + newSession.getUserID()); + + // monitoring + activeSessions.put(thread.getId(), newSession); + threads.add(thread); + return newSession; + } + + public Object getObject() { + return proxiedSession; + } + + public void init() throws Exception { + // log.error("SHOULD NOT BE USED ANYMORE"); + monitoringThread = new MonitoringThread(); + monitoringThread.start(); + } + + public void dispose() throws Exception { + // if (activeSessions.size() == 0) + // return; + + if (log.isTraceEnabled()) + log.trace("Cleaning up " + activeSessions.size() + " active JCR sessions..."); + + deactivate(); + for (Session sess : activeSessions.values()) { + JcrUtils.logoutQuietly(sess); + } + activeSessions.clear(); + } + + protected Boolean isActive() { + return active; + } + + protected synchronized void deactivate() { + active = false; + notifyAll(); + } + + protected synchronized void removeSession(Thread thread) { + if (!isActive()) + return; + activeSessions.remove(thread.getId()); + threads.remove(thread); + } + + protected synchronized void cleanDeadThreads() { + if (!isActive()) + return; + Iterator it = threads.iterator(); + while (it.hasNext()) { + Thread thread = it.next(); + if (!thread.isAlive() && isActive()) { + if (activeSessions.containsKey(thread.getId())) { + Session session = activeSessions.get(thread.getId()); + activeSessions.remove(thread.getId()); + session.logout(); + if (log.isTraceEnabled()) + log.trace("Cleaned up JCR session (userID=" + session.getUserID() + ") from dead thread " + + thread.getId()); + } + it.remove(); + } + } + try { + wait(1000); + } catch (InterruptedException e) { + // silent + } + } + + public Class getObjectType() { + return Session.class; + } + + public boolean isSingleton() { + return true; + } + + /** + * Called before a method is actually called, allowing to check the session + * or re-login it (e.g. if authentication has changed). The default + * implementation returns the session. + */ + protected Session preCall(Session session) { + return session; + } + + protected Repository repository() { + if (repository != null) + return repository; + if (repositories != null) { + // hardened for OSGi dynamic services + Iterator it = repositories.iterator(); + if (it.hasNext()) + return it.next(); + } + throw new ArgeoJcrException("No repository injected"); + } + + // /** Useful for declarative registration of OSGi services (blueprint) */ + // public void register(Repository repository, Map params) { + // this.repository = repository; + // } + // + // /** Useful for declarative registration of OSGi services (blueprint) */ + // public void unregister(Repository repository, Map params) { + // this.repository = null; + // } + + public void setRepository(Repository repository) { + this.repository = repository; + } + + public void setRepositories(List repositories) { + this.repositories = repositories; + } + + public void setDefaultUsername(String defaultUsername) { + this.defaultUsername = defaultUsername; + } + + public void setDefaultPassword(String defaultPassword) { + this.defaultPassword = defaultPassword; + } + + public void setForceDefaultCredentials(Boolean forceDefaultCredentials) { + this.forceDefaultCredentials = forceDefaultCredentials; + } + + public void setWorkspace(String workspace) { + this.workspace = workspace; + } + + protected class JcrSessionInvocationHandler implements InvocationHandler { + + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable, RepositoryException { + Session threadSession = session.get(); + if (threadSession == null) { + if ("logout".equals(method.getName()))// no need to login + return Void.TYPE; + else if ("toString".equals(method.getName()))// maybe logging + return "Uninitialized Argeo thread bound JCR session"; + threadSession = login(); + } + + preCall(threadSession); + Object ret; + try { + ret = method.invoke(threadSession, args); + } catch (InvocationTargetException e) { + Throwable cause = e.getCause(); + if (cause instanceof RepositoryException) + throw (RepositoryException) cause; + else + throw cause; + } + if ("logout".equals(method.getName())) { + session.remove(); + Thread thread = Thread.currentThread(); + removeSession(thread); + if (log.isTraceEnabled()) + log.trace("Logged out JCR session (userId=" + threadSession.getUserID() + ") on thread " + + thread.getId()); + } + return ret; + } + } + + /** Monitors registered thread in order to clean up dead ones. */ + private class MonitoringThread extends Thread { + + public MonitoringThread() { + super("ThreadBound JCR Session Monitor"); + } + + @Override + public void run() { + while (isActive()) { + cleanDeadThreads(); + } + } + + } +} diff --git a/org.argeo.jcr/src/org/argeo/jcr/UserJcrUtils.java b/org.argeo.jcr/src/org/argeo/jcr/UserJcrUtils.java new file mode 100644 index 000000000..95269475e --- /dev/null +++ b/org.argeo.jcr/src/org/argeo/jcr/UserJcrUtils.java @@ -0,0 +1,98 @@ +/* + * 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.jcr; + +import javax.jcr.Node; +import javax.jcr.RepositoryException; +import javax.jcr.Session; +import javax.jcr.query.Query; +import javax.jcr.query.qom.Constraint; +import javax.jcr.query.qom.DynamicOperand; +import javax.jcr.query.qom.QueryObjectModelFactory; +import javax.jcr.query.qom.Selector; +import javax.jcr.query.qom.StaticOperand; + +/** + * Utilities related to the user home and properties based on Argeo JCR model. + * Do not use anymore. Does not fit with current security model + */ +@Deprecated +public class UserJcrUtils { + /** The home base path. Not yet configurable */ + public final static String DEFAULT_HOME_BASE_PATH = "/home"; + + /** + * Returns the home node of the user or null if none was found. + * + * @param session + * the session to use in order to perform the search, this can be + * a session with a different user ID than the one searched, + * typically when a system or admin session is used. + * @param username + * the username of the user + */ + public static Node getUserHome(Session session, String username) { + try { + // String homePath = UserJcrUtils.getUserHomePath(username); + // return session.itemExists(homePath) ? session.getNode(homePath) + // : null; + // kept for example of QOM queries + QueryObjectModelFactory qomf = session.getWorkspace() + .getQueryManager().getQOMFactory(); + Selector userHomeSel = qomf.selector(ArgeoTypes.ARGEO_USER_HOME, + "userHome"); + DynamicOperand userIdDop = qomf.propertyValue( + userHomeSel.getSelectorName(), ArgeoNames.ARGEO_USER_ID); + StaticOperand userIdSop = qomf.literal(session.getValueFactory() + .createValue(username)); + Constraint constraint = qomf.comparison(userIdDop, + QueryObjectModelFactory.JCR_OPERATOR_EQUAL_TO, userIdSop); + Query query = qomf.createQuery(userHomeSel, constraint, null, null); + return JcrUtils.querySingleNode(query); + } catch (RepositoryException e) { + throw new ArgeoJcrException("Cannot find home for user " + username, e); + } + } + + public static Node getUserProfile(Session session, String username) { + try { + QueryObjectModelFactory qomf = session.getWorkspace() + .getQueryManager().getQOMFactory(); + Selector userHomeSel = qomf.selector(ArgeoTypes.ARGEO_USER_PROFILE, + "userProfile"); + DynamicOperand userIdDop = qomf.propertyValue( + userHomeSel.getSelectorName(), ArgeoNames.ARGEO_USER_ID); + StaticOperand userIdSop = qomf.literal(session.getValueFactory() + .createValue(username)); + Constraint constraint = qomf.comparison(userIdDop, + QueryObjectModelFactory.JCR_OPERATOR_EQUAL_TO, userIdSop); + Query query = qomf.createQuery(userHomeSel, constraint, null, null); + return JcrUtils.querySingleNode(query); + } catch (RepositoryException e) { + throw new ArgeoJcrException( + "Cannot find profile for user " + username, e); + } + } + + /** Returns the home node of the session user or null if none was found. */ + public static Node getUserHome(Session session) { + String userID = session.getUserID(); + return getUserHome(session, userID); + } + + private UserJcrUtils() { + } +} diff --git a/org.argeo.jcr/src/org/argeo/jcr/VersionDiff.java b/org.argeo.jcr/src/org/argeo/jcr/VersionDiff.java new file mode 100644 index 000000000..e6ae9130b --- /dev/null +++ b/org.argeo.jcr/src/org/argeo/jcr/VersionDiff.java @@ -0,0 +1,53 @@ +/* + * 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.jcr; + +import java.util.Calendar; +import java.util.Map; + +/** + * Generic Object that enables the creation of history reports based on a JCR + * versionable node. userId and creation date are added to the map of + * PropertyDiff. + * + * These two fields might be null + * + */ +public class VersionDiff { + + private String userId; + private Map diffs; + private Calendar updateTime; + + public VersionDiff(String userId, Calendar updateTime, + Map diffs) { + this.userId = userId; + this.updateTime = updateTime; + this.diffs = diffs; + } + + public String getUserId() { + return userId; + } + + public Map getDiffs() { + return diffs; + } + + public Calendar getUpdateTime() { + return updateTime; + } +} diff --git a/org.argeo.jcr/src/org/argeo/jcr/argeo.cnd b/org.argeo.jcr/src/org/argeo/jcr/argeo.cnd new file mode 100644 index 000000000..fbfea9dd9 --- /dev/null +++ b/org.argeo.jcr/src/org/argeo/jcr/argeo.cnd @@ -0,0 +1,72 @@ + + +// GENERIC TYPES NOT AVAILABLE IN JCR +[argeo:link] > mix:created, mix:lastModified +mixin +// URI(s) +- argeo:uri (STRING) m + +[argeo:references] > nt:unstructured +- * (REFERENCE) * + +// DATA MODEL +[argeo:dataModel] > mix:created, mix:lastModified, mix:versionable +mixin +- argeo:uri (STRING) m +- argeo:dataModelVersion (STRING) m + +// USER NODES +// user should be lower case, between 3 and 15 characters long +[argeo:userHome] > mix:created, mix:lastModified +mixin +- argeo:userID (STRING) m +- argeo:remoteRoles (STRING) * +// deprecated. for backward compatibility: ++ argeo:profile (argeo:userProfile) ++ argeo:keyring (argeo:pbeSpec) ++ argeo:preferences (argeo:preferenceNode) + +[argeo:userProfile] > mix:created, mix:lastModified, mix:title, mix:versionable +mixin +- argeo:userID (STRING) m +- argeo:enabled (BOOLEAN) +- argeo:accountNonExpired (BOOLEAN) +- argeo:accountNonLocked (BOOLEAN) +- argeo:credentialsNonExpired (BOOLEAN) + +[argeo:preferenceNode] > mix:lastModified, mix:versionable +mixin ++ * (argeo:preferenceNode) * version + +[argeo:remoteRepository] > nt:unstructured +- argeo:uri (STRING) +- argeo:userID (STRING) ++ argeo:password (argeo:encrypted) + +// TABULAR CONTENT +[argeo:table] > nt:file ++ * (argeo:column) * + +[argeo:column] > mix:title +- jcr:requiredType (STRING) = 'STRING' + +[argeo:csv] > nt:resource + +// CRYPTO +[argeo:encrypted] > nt:base +mixin +// initialization vector used by some algorithms +- argeo:iv (BINARY) + +[argeo:pbeKeySpec] > nt:base +mixin +- argeo:secretKeyFactory (STRING) +- argeo:salt (BINARY) +- argeo:iterationCount (LONG) +- argeo:keyLength (LONG) +- argeo:secretKeyEncryption (STRING) + +[argeo:pbeSpec] > argeo:pbeKeySpec +mixin +- argeo:cipher (STRING) + diff --git a/org.argeo.jcr/src/org/argeo/jcr/docbook/DocBookModel.java b/org.argeo.jcr/src/org/argeo/jcr/docbook/DocBookModel.java new file mode 100644 index 000000000..61a902d9e --- /dev/null +++ b/org.argeo.jcr/src/org/argeo/jcr/docbook/DocBookModel.java @@ -0,0 +1,42 @@ +package org.argeo.jcr.docbook; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +import javax.jcr.ImportUUIDBehavior; +import javax.jcr.RepositoryException; +import javax.jcr.Session; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +public class DocBookModel { + private final static Log log = LogFactory.getLog(DocBookModel.class); + private Session session; + + public DocBookModel(Session session) { + super(); + this.session = session; + } + + public void setSession(Session session) { + this.session = session; + } + + public void importXml(String path, InputStream in) + throws RepositoryException, IOException { + long begin = System.currentTimeMillis(); + session.importXML(path, in, + ImportUUIDBehavior.IMPORT_UUID_COLLISION_REPLACE_EXISTING); + long duration = System.currentTimeMillis() - begin; + if (log.isTraceEnabled()) + log.trace("Imported " + path + " in " + duration + " ms"); + + } + + public void exportXml(String path, OutputStream out) + throws RepositoryException, IOException { + session.exportDocumentView(path, out, true, false); + } +} diff --git a/org.argeo.jcr/src/org/argeo/jcr/docbook/DocBookNames.java b/org.argeo.jcr/src/org/argeo/jcr/docbook/DocBookNames.java new file mode 100644 index 000000000..f4edb12b3 --- /dev/null +++ b/org.argeo.jcr/src/org/argeo/jcr/docbook/DocBookNames.java @@ -0,0 +1,7 @@ +package org.argeo.jcr.docbook; + +public interface DocBookNames { + public final static String DBK_ = "dbk:"; + public final static String DBK_PARA = DBK_ + "para"; + public final static String DBK_SECTION = DBK_ + "section"; +} diff --git a/org.argeo.jcr/src/org/argeo/jcr/docbook/docbook-full.cnd b/org.argeo.jcr/src/org/argeo/jcr/docbook/docbook-full.cnd new file mode 100644 index 000000000..7e4fefab9 --- /dev/null +++ b/org.argeo.jcr/src/org/argeo/jcr/docbook/docbook-full.cnd @@ -0,0 +1,3005 @@ + + + + + + + + +[argeodbk:titled] +mixin + + dbk:info (dbk:info) = dbk:info * + + dbk:title (dbk:title) = dbk:title * + + dbk:titleabbrev (dbk:titleabbrev) = dbk:titleabbrev * + +[argeodbk:linkingAttributes] +mixin + - dbk:linkend (String) + - xlink:actuate (String) + - xlink:arcrole (String) + - xlink:href (String) + - xlink:role (String) + - xlink:show (String) + - xlink:title (String) + - xlink:type (String) + +[argeodbk:freeText] +mixin + + dbk:phrase (dbk:phrase) = dbk:phrase * + + dbk:replaceable (dbk:replaceable) = dbk:replaceable * + + jcr:xmltext (dbk:xmltext) = dbk:xmltext * + +[argeodbk:markupInlines] +mixin + + dbk:code (dbk:code) = dbk:code * + + dbk:constant (dbk:constant) = dbk:constant * + + dbk:email (dbk:email) = dbk:email * + + dbk:literal (dbk:literal) = dbk:literal * + + dbk:markup (dbk:markup) = dbk:markup * + + dbk:symbol (dbk:symbol) = dbk:symbol * + + dbk:tag (dbk:tag) = dbk:tag * + + dbk:token (dbk:token) = dbk:token * + + dbk:uri (dbk:uri) = dbk:uri * + +[argeodbk:listElements] +mixin + + dbk:bibliolist (dbk:bibliolist) = dbk:bibliolist * + + dbk:calloutlist (dbk:calloutlist) = dbk:calloutlist * + + dbk:glosslist (dbk:glosslist) = dbk:glosslist * + + dbk:itemizedlist (dbk:itemizedlist) = dbk:itemizedlist * + + dbk:orderedlist (dbk:orderedlist) = dbk:orderedlist * + + dbk:procedure (dbk:procedure) = dbk:procedure * + + dbk:qandaset (dbk:qandaset) = dbk:qandaset * + + dbk:segmentedlist (dbk:segmentedlist) = dbk:segmentedlist * + + dbk:simplelist (dbk:simplelist) = dbk:simplelist * + + dbk:variablelist (dbk:variablelist) = dbk:variablelist * + +[argeodbk:paragraphElements] +mixin + + dbk:formalpara (dbk:formalpara) = dbk:formalpara * + + dbk:para (dbk:para) = dbk:para * + + dbk:simpara (dbk:simpara) = dbk:simpara * + +[argeodbk:indexingInlines] +mixin + + dbk:indexterm (dbk:indexterm) = dbk:indexterm * + +[argeodbk:techDocElements] +mixin + + dbk:caution (dbk:caution) = dbk:caution * + + dbk:classsynopsis (dbk:classsynopsis) = dbk:classsynopsis * + + dbk:cmdsynopsis (dbk:cmdsynopsis) = dbk:cmdsynopsis * + + dbk:constraintdef (dbk:constraintdef) = dbk:constraintdef * + + dbk:constructorsynopsis (dbk:constructorsynopsis) = dbk:constructorsynopsis * + + dbk:destructorsynopsis (dbk:destructorsynopsis) = dbk:destructorsynopsis * + + dbk:equation (dbk:equation) = dbk:equation * + + dbk:example (dbk:example) = dbk:example * + + dbk:fieldsynopsis (dbk:fieldsynopsis) = dbk:fieldsynopsis * + + dbk:figure (dbk:figure) = dbk:figure * + + dbk:funcsynopsis (dbk:funcsynopsis) = dbk:funcsynopsis * + + dbk:important (dbk:important) = dbk:important * + + dbk:informalequation (dbk:informalequation) = dbk:informalequation * + + dbk:informalexample (dbk:informalexample) = dbk:informalexample * + + dbk:informalfigure (dbk:informalfigure) = dbk:informalfigure * + + dbk:informaltable (dbk:informaltable) = dbk:informaltable * + + dbk:literallayout (dbk:literallayout) = dbk:literallayout * + + dbk:methodsynopsis (dbk:methodsynopsis) = dbk:methodsynopsis * + + dbk:msgset (dbk:msgset) = dbk:msgset * + + dbk:note (dbk:note) = dbk:note * + + dbk:productionset (dbk:productionset) = dbk:productionset * + + dbk:programlisting (dbk:programlisting) = dbk:programlisting * + + dbk:programlistingco (dbk:programlistingco) = dbk:programlistingco * + + dbk:screen (dbk:screen) = dbk:screen * + + dbk:screenco (dbk:screenco) = dbk:screenco * + + dbk:synopsis (dbk:synopsis) = dbk:synopsis * + + dbk:table (dbk:table) = dbk:table * + + dbk:task (dbk:task) = dbk:task * + + dbk:tip (dbk:tip) = dbk:tip * + + dbk:warning (dbk:warning) = dbk:warning * + +[argeodbk:techDocInlines] +mixin + + dbk:accel (dbk:accel) = dbk:accel * + + dbk:application (dbk:application) = dbk:application * + + dbk:classname (dbk:classname) = dbk:classname * + + dbk:command (dbk:command) = dbk:command * + + dbk:computeroutput (dbk:computeroutput) = dbk:computeroutput * + + dbk:database (dbk:database) = dbk:database * + + dbk:envar (dbk:envar) = dbk:envar * + + dbk:errorcode (dbk:errorcode) = dbk:errorcode * + + dbk:errorname (dbk:errorname) = dbk:errorname * + + dbk:errortext (dbk:errortext) = dbk:errortext * + + dbk:errortype (dbk:errortype) = dbk:errortype * + + dbk:exceptionname (dbk:exceptionname) = dbk:exceptionname * + + dbk:filename (dbk:filename) = dbk:filename * + + dbk:function (dbk:function) = dbk:function * + + dbk:guibutton (dbk:guibutton) = dbk:guibutton * + + dbk:guiicon (dbk:guiicon) = dbk:guiicon * + + dbk:guilabel (dbk:guilabel) = dbk:guilabel * + + dbk:guimenu (dbk:guimenu) = dbk:guimenu * + + dbk:guimenuitem (dbk:guimenuitem) = dbk:guimenuitem * + + dbk:guisubmenu (dbk:guisubmenu) = dbk:guisubmenu * + + dbk:hardware (dbk:hardware) = dbk:hardware * + + dbk:initializer (dbk:initializer) = dbk:initializer * + + dbk:inlineequation (dbk:inlineequation) = dbk:inlineequation * + + dbk:interfacename (dbk:interfacename) = dbk:interfacename * + + dbk:keycap (dbk:keycap) = dbk:keycap * + + dbk:keycode (dbk:keycode) = dbk:keycode * + + dbk:keycombo (dbk:keycombo) = dbk:keycombo * + + dbk:keysym (dbk:keysym) = dbk:keysym * + + dbk:menuchoice (dbk:menuchoice) = dbk:menuchoice * + + dbk:methodname (dbk:methodname) = dbk:methodname * + + dbk:modifier (dbk:modifier) = dbk:modifier * + + dbk:mousebutton (dbk:mousebutton) = dbk:mousebutton * + + dbk:nonterminal (dbk:nonterminal) = dbk:nonterminal * + + dbk:ooclass (dbk:ooclass) = dbk:ooclass * + + dbk:ooexception (dbk:ooexception) = dbk:ooexception * + + dbk:oointerface (dbk:oointerface) = dbk:oointerface * + + dbk:option (dbk:option) = dbk:option * + + dbk:optional (dbk:optional) = dbk:optional * + + dbk:package (dbk:package) = dbk:package * + + dbk:parameter (dbk:parameter) = dbk:parameter * + + dbk:productname (dbk:productname) = dbk:productname * + + dbk:productnumber (dbk:productnumber) = dbk:productnumber * + + dbk:prompt (dbk:prompt) = dbk:prompt * + + dbk:property (dbk:property) = dbk:property * + + dbk:returnvalue (dbk:returnvalue) = dbk:returnvalue * + + dbk:shortcut (dbk:shortcut) = dbk:shortcut * + + dbk:systemitem (dbk:systemitem) = dbk:systemitem * + + dbk:termdef (dbk:termdef) = dbk:termdef * + + dbk:trademark (dbk:trademark) = dbk:trademark * + + dbk:type (dbk:type) = dbk:type * + + dbk:userinput (dbk:userinput) = dbk:userinput * + + dbk:varname (dbk:varname) = dbk:varname * + +[argeodbk:publishingElements] +mixin + + dbk:address (dbk:address) = dbk:address * + + dbk:blockquote (dbk:blockquote) = dbk:blockquote * + + dbk:epigraph (dbk:epigraph) = dbk:epigraph * + + dbk:sidebar (dbk:sidebar) = dbk:sidebar * + +[argeodbk:ubiquitousInlines] +mixin + + dbk:alt (dbk:alt) = dbk:alt * + + dbk:anchor (dbk:anchor) = dbk:anchor * + + dbk:annotation (dbk:annotation) = dbk:annotation * + + dbk:biblioref (dbk:biblioref) = dbk:biblioref * + + dbk:inlinemediaobject (dbk:inlinemediaobject) = dbk:inlinemediaobject * + + dbk:link (dbk:link) = dbk:link * + + dbk:olink (dbk:olink) = dbk:olink * + + dbk:remark (dbk:remark) = dbk:remark * + + dbk:subscript (dbk:subscript) = dbk:subscript * + + dbk:superscript (dbk:superscript) = dbk:superscript * + + dbk:xref (dbk:xref) = dbk:xref * + +[argeodbk:abstractSection] +mixin + + dbk:annotation (dbk:annotation) = dbk:annotation * + + dbk:bibliography (dbk:bibliography) = dbk:bibliography * + + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead * + + dbk:glossary (dbk:glossary) = dbk:glossary * + + dbk:index (dbk:index) = dbk:index * + + dbk:remark (dbk:remark) = dbk:remark * + + dbk:revhistory (dbk:revhistory) = dbk:revhistory * + + dbk:subtitle (dbk:subtitle) = dbk:subtitle * + + dbk:toc (dbk:toc) = dbk:toc * + - dbk:label (String) + - dbk:status (String) + +[argeodbk:bibliographyInlines] +mixin + + dbk:author (dbk:author) = dbk:author * + + dbk:citation (dbk:citation) = dbk:citation * + + dbk:citebiblioid (dbk:citebiblioid) = dbk:citebiblioid * + + dbk:citerefentry (dbk:citerefentry) = dbk:citerefentry * + + dbk:citetitle (dbk:citetitle) = dbk:citetitle * + + dbk:editor (dbk:editor) = dbk:editor * + + dbk:jobtitle (dbk:jobtitle) = dbk:jobtitle * + + dbk:org (dbk:org) = dbk:org * + + dbk:orgname (dbk:orgname) = dbk:orgname * + + dbk:person (dbk:person) = dbk:person * + + dbk:personname (dbk:personname) = dbk:personname * + +[argeodbk:publishingInlines] +mixin + + dbk:abbrev (dbk:abbrev) = dbk:abbrev * + + dbk:acronym (dbk:acronym) = dbk:acronym * + + dbk:coref (dbk:coref) = dbk:coref * + + dbk:date (dbk:date) = dbk:date * + + dbk:emphasis (dbk:emphasis) = dbk:emphasis * + + dbk:firstterm (dbk:firstterm) = dbk:firstterm * + + dbk:footnote (dbk:footnote) = dbk:footnote * + + dbk:footnoteref (dbk:footnoteref) = dbk:footnoteref * + + dbk:foreignphrase (dbk:foreignphrase) = dbk:foreignphrase * + + dbk:glossterm (dbk:glossterm) = dbk:glossterm * + + dbk:quote (dbk:quote) = dbk:quote * + + dbk:wordasword (dbk:wordasword) = dbk:wordasword * + +[argeodbk:base] +abstract + - dbk:annotations (String) + - dbk:arch (String) + - dbk:audience (String) + - dbk:condition (String) + - dbk:conformance (String) + - dbk:dir (String) + - dbk:os (String) + - dbk:remap (String) + - dbk:revision (String) + - dbk:revisionflag (String) + - dbk:role (String) + - dbk:security (String) + - dbk:userlevel (String) + - dbk:vendor (String) + - dbk:version (String) + - dbk:wordsize (String) + - dbk:xreflabel (String) + - xml:base (String) + - xml:id (String) + - xml:lang (String) + +[dbk:abbrev] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines + + dbk:trademark (dbk:trademark) = dbk:trademark * + +[dbk:abstract] > argeodbk:base, argeodbk:linkingAttributes, argeodbk:paragraphElements, argeodbk:titled + + dbk:anchor (dbk:anchor) = dbk:anchor * + +[dbk:accel] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines + +[dbk:acknowledgements] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled + + dbk:anchor (dbk:anchor) = dbk:anchor * + + dbk:annotation (dbk:annotation) = dbk:annotation * + + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead * + + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject * + + dbk:remark (dbk:remark) = dbk:remark * + + dbk:revhistory (dbk:revhistory) = dbk:revhistory * + + dbk:screenshot (dbk:screenshot) = dbk:screenshot * + + dbk:subtitle (dbk:subtitle) = dbk:subtitle * + - dbk:label (String) + - dbk:status (String) + +[dbk:acronym] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines + + dbk:trademark (dbk:trademark) = dbk:trademark * + +[dbk:address] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines + + dbk:city (dbk:city) = dbk:city * + + dbk:country (dbk:country) = dbk:country * + + dbk:email (dbk:email) = dbk:email * + + dbk:fax (dbk:fax) = dbk:fax * + + dbk:otheraddr (dbk:otheraddr) = dbk:otheraddr * + + dbk:personname (dbk:personname) = dbk:personname * + + dbk:phone (dbk:phone) = dbk:phone * + + dbk:pob (dbk:pob) = dbk:pob * + + dbk:postcode (dbk:postcode) = dbk:postcode * + + dbk:state (dbk:state) = dbk:state * + + dbk:street (dbk:street) = dbk:street * + + dbk:uri (dbk:uri) = dbk:uri * + - dbk:continuation (String) + - dbk:language (String) + - dbk:linenumbering (String) + - dbk:startinglinenumber (String) + - xml:space (String) + +[dbk:affiliation] > argeodbk:base, argeodbk:linkingAttributes + + dbk:address (dbk:address) = dbk:address * + + dbk:jobtitle (dbk:jobtitle) = dbk:jobtitle * + + dbk:org (dbk:org) = dbk:org + + dbk:orgdiv (dbk:orgdiv) = dbk:orgdiv * + + dbk:orgname (dbk:orgname) = dbk:orgname + + dbk:shortaffil (dbk:shortaffil) = dbk:shortaffil + +[dbk:alt] > argeodbk:base + + dbk:inlinemediaobject (dbk:inlinemediaobject) = dbk:inlinemediaobject * + + jcr:xmltext (dbk:xmltext) = dbk:xmltext * + +[dbk:anchor] > argeodbk:base + +[dbk:annotation] > argeodbk:base, argeodbk:indexingInlines, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled + + dbk:anchor (dbk:anchor) = dbk:anchor * + + dbk:annotation (dbk:annotation) = dbk:annotation * + + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead * + + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject * + + dbk:remark (dbk:remark) = dbk:remark * + + dbk:revhistory (dbk:revhistory) = dbk:revhistory * + + dbk:screenshot (dbk:screenshot) = dbk:screenshot * + - dbk:annotates (String) + +[dbk:answer] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements + + dbk:anchor (dbk:anchor) = dbk:anchor * + + dbk:annotation (dbk:annotation) = dbk:annotation * + + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead * + + dbk:label (dbk:label) = dbk:label + + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject * + + dbk:remark (dbk:remark) = dbk:remark * + + dbk:revhistory (dbk:revhistory) = dbk:revhistory * + + dbk:screenshot (dbk:screenshot) = dbk:screenshot * + +[dbk:appendix] > argeodbk:abstractSection, argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled + + dbk:anchor (dbk:anchor) = dbk:anchor * + + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject * + + dbk:refentry (dbk:refentry) = dbk:refentry * + + dbk:screenshot (dbk:screenshot) = dbk:screenshot * + + dbk:sect1 (dbk:sect1) = dbk:sect1 * + + dbk:section (dbk:section) = dbk:section * + + dbk:simplesect (dbk:simplesect) = dbk:simplesect * + + dbk:simplesect (dbk:simplesect) = dbk:simplesect * + +[dbk:application] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines + - dbk:class (String) + +[dbk:arc] > argeodbk:base + - xlink:from (String) + - xlink:to (String) + +[dbk:area] > argeodbk:base + + dbk:alt (dbk:alt) = dbk:alt + - dbk:coords (String) + - dbk:label (String) + - dbk:linkends (String) + - dbk:otherunits (String) + - dbk:units (String) + +[dbk:areaset] > argeodbk:base + + dbk:area (dbk:area) = dbk:area * + - dbk:label (String) + - dbk:linkends (String) + - dbk:otherunits (String) + - dbk:units (String) + +[dbk:areaspec] > argeodbk:base, argeodbk:linkingAttributes + + dbk:area (dbk:area) = dbk:area * + + dbk:areaset (dbk:areaset) = dbk:areaset * + - dbk:otherunits (String) + - dbk:units (String) + +[dbk:arg] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines + + dbk:arg (dbk:arg) = dbk:arg * + + dbk:group (dbk:group) = dbk:group * + + dbk:option (dbk:option) = dbk:option * + + dbk:sbr (dbk:sbr) = dbk:sbr * + + dbk:synopfragmentref (dbk:synopfragmentref) = dbk:synopfragmentref * + - dbk:choice (String) + - dbk:rep (String) + +[dbk:article] > argeodbk:abstractSection, argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled + + dbk:acknowledgements (dbk:acknowledgements) = dbk:acknowledgements * + + dbk:anchor (dbk:anchor) = dbk:anchor * + + dbk:appendix (dbk:appendix) = dbk:appendix * + + dbk:colophon (dbk:colophon) = dbk:colophon * + + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject * + + dbk:refentry (dbk:refentry) = dbk:refentry * + + dbk:screenshot (dbk:screenshot) = dbk:screenshot * + + dbk:sect1 (dbk:sect1) = dbk:sect1 * + + dbk:section (dbk:section) = dbk:section * + + dbk:simplesect (dbk:simplesect) = dbk:simplesect * + + dbk:simplesect (dbk:simplesect) = dbk:simplesect * + - dbk:class (String) + +[dbk:artpagenums] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines + +[dbk:attribution] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines + + dbk:citation (dbk:citation) = dbk:citation * + + dbk:citetitle (dbk:citetitle) = dbk:citetitle * + + dbk:person (dbk:person) = dbk:person * + + dbk:personname (dbk:personname) = dbk:personname * + +[dbk:audiodata] > argeodbk:base + + dbk:info (dbk:info) = dbk:info + - dbk:entityref (String) + - dbk:fileref (String) + - dbk:format (String) + +[dbk:audioobject] > argeodbk:base, argeodbk:linkingAttributes + + dbk:audiodata (dbk:audiodata) = dbk:audiodata + + dbk:info (dbk:info) = dbk:info + +[dbk:author] > argeodbk:base, argeodbk:linkingAttributes + + dbk:address (dbk:address) = dbk:address * + + dbk:affiliation (dbk:affiliation) = dbk:affiliation * + + dbk:contrib (dbk:contrib) = dbk:contrib * + + dbk:email (dbk:email) = dbk:email * + + dbk:orgdiv (dbk:orgdiv) = dbk:orgdiv * + + dbk:orgname (dbk:orgname) = dbk:orgname + + dbk:personblurb (dbk:personblurb) = dbk:personblurb * + + dbk:personname (dbk:personname) = dbk:personname + + dbk:uri (dbk:uri) = dbk:uri * + +[dbk:authorgroup] > argeodbk:base, argeodbk:linkingAttributes + + dbk:author (dbk:author) = dbk:author * + + dbk:editor (dbk:editor) = dbk:editor * + + dbk:othercredit (dbk:othercredit) = dbk:othercredit * + +[dbk:authorinitials] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines + +[dbk:bibliocoverage] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines + - dbk:otherspatial (String) + - dbk:othertemporal (String) + - dbk:spatial (String) + - dbk:temporal (String) + +[dbk:bibliodiv] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled + + dbk:anchor (dbk:anchor) = dbk:anchor * + + dbk:annotation (dbk:annotation) = dbk:annotation * + + dbk:biblioentry (dbk:biblioentry) = dbk:biblioentry * + + dbk:bibliomixed (dbk:bibliomixed) = dbk:bibliomixed * + + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead * + + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject * + + dbk:remark (dbk:remark) = dbk:remark * + + dbk:revhistory (dbk:revhistory) = dbk:revhistory * + + dbk:screenshot (dbk:screenshot) = dbk:screenshot * + + dbk:subtitle (dbk:subtitle) = dbk:subtitle * + - dbk:label (String) + - dbk:status (String) + +[dbk:biblioentry] > argeodbk:base, argeodbk:linkingAttributes, argeodbk:publishingInlines + + dbk:abstract (dbk:abstract) = dbk:abstract * + + dbk:address (dbk:address) = dbk:address * + + dbk:annotation (dbk:annotation) = dbk:annotation * + + dbk:artpagenums (dbk:artpagenums) = dbk:artpagenums * + + dbk:author (dbk:author) = dbk:author * + + dbk:authorgroup (dbk:authorgroup) = dbk:authorgroup * + + dbk:authorinitials (dbk:authorinitials) = dbk:authorinitials * + + dbk:bibliocoverage (dbk:bibliocoverage) = dbk:bibliocoverage * + + dbk:biblioid (dbk:biblioid) = dbk:biblioid * + + dbk:bibliomisc (dbk:bibliomisc) = dbk:bibliomisc * + + dbk:bibliomset (dbk:bibliomset) = dbk:bibliomset * + + dbk:bibliorelation (dbk:bibliorelation) = dbk:bibliorelation * + + dbk:biblioset (dbk:biblioset) = dbk:biblioset * + + dbk:bibliosource (dbk:bibliosource) = dbk:bibliosource * + + dbk:citebiblioid (dbk:citebiblioid) = dbk:citebiblioid * + + dbk:citerefentry (dbk:citerefentry) = dbk:citerefentry * + + dbk:citetitle (dbk:citetitle) = dbk:citetitle * + + dbk:collab (dbk:collab) = dbk:collab * + + dbk:confgroup (dbk:confgroup) = dbk:confgroup * + + dbk:contractnum (dbk:contractnum) = dbk:contractnum * + + dbk:contractsponsor (dbk:contractsponsor) = dbk:contractsponsor * + + dbk:copyright (dbk:copyright) = dbk:copyright * + + dbk:cover (dbk:cover) = dbk:cover * + + dbk:edition (dbk:edition) = dbk:edition * + + dbk:editor (dbk:editor) = dbk:editor * + + dbk:extendedlink (dbk:extendedlink) = dbk:extendedlink * + + dbk:issuenum (dbk:issuenum) = dbk:issuenum * + + dbk:itermset (dbk:itermset) = dbk:itermset * + + dbk:keywordset (dbk:keywordset) = dbk:keywordset * + + dbk:legalnotice (dbk:legalnotice) = dbk:legalnotice * + + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject * + + dbk:org (dbk:org) = dbk:org * + + dbk:orgname (dbk:orgname) = dbk:orgname * + + dbk:othercredit (dbk:othercredit) = dbk:othercredit * + + dbk:pagenums (dbk:pagenums) = dbk:pagenums * + + dbk:person (dbk:person) = dbk:person * + + dbk:personblurb (dbk:personblurb) = dbk:personblurb * + + dbk:personname (dbk:personname) = dbk:personname * + + dbk:phrase (dbk:phrase) = dbk:phrase * + + dbk:printhistory (dbk:printhistory) = dbk:printhistory * + + dbk:productname (dbk:productname) = dbk:productname * + + dbk:productnumber (dbk:productnumber) = dbk:productnumber * + + dbk:pubdate (dbk:pubdate) = dbk:pubdate * + + dbk:publisher (dbk:publisher) = dbk:publisher * + + dbk:publishername (dbk:publishername) = dbk:publishername * + + dbk:releaseinfo (dbk:releaseinfo) = dbk:releaseinfo * + + dbk:revhistory (dbk:revhistory) = dbk:revhistory * + + dbk:seriesvolnums (dbk:seriesvolnums) = dbk:seriesvolnums * + + dbk:subjectset (dbk:subjectset) = dbk:subjectset * + + dbk:subscript (dbk:subscript) = dbk:subscript * + + dbk:subtitle (dbk:subtitle) = dbk:subtitle * + + dbk:superscript (dbk:superscript) = dbk:superscript * + + dbk:title (dbk:title) = dbk:title * + + dbk:titleabbrev (dbk:titleabbrev) = dbk:titleabbrev * + + dbk:volumenum (dbk:volumenum) = dbk:volumenum * + +[dbk:bibliography] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled + + dbk:anchor (dbk:anchor) = dbk:anchor * + + dbk:annotation (dbk:annotation) = dbk:annotation * + + dbk:bibliodiv (dbk:bibliodiv) = dbk:bibliodiv * + + dbk:biblioentry (dbk:biblioentry) = dbk:biblioentry * + + dbk:bibliomixed (dbk:bibliomixed) = dbk:bibliomixed * + + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead * + + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject * + + dbk:remark (dbk:remark) = dbk:remark * + + dbk:revhistory (dbk:revhistory) = dbk:revhistory * + + dbk:screenshot (dbk:screenshot) = dbk:screenshot * + + dbk:subtitle (dbk:subtitle) = dbk:subtitle * + - dbk:label (String) + - dbk:status (String) + +[dbk:biblioid] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines + - dbk:class (String) + - dbk:otherclass (String) + +[dbk:bibliolist] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled + + dbk:anchor (dbk:anchor) = dbk:anchor * + + dbk:annotation (dbk:annotation) = dbk:annotation * + + dbk:biblioentry (dbk:biblioentry) = dbk:biblioentry * + + dbk:bibliomixed (dbk:bibliomixed) = dbk:bibliomixed * + + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead * + + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject * + + dbk:remark (dbk:remark) = dbk:remark * + + dbk:revhistory (dbk:revhistory) = dbk:revhistory * + + dbk:screenshot (dbk:screenshot) = dbk:screenshot * + +[dbk:bibliomisc] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines + +[dbk:bibliomixed] > argeodbk:base, argeodbk:linkingAttributes, argeodbk:publishingInlines + + dbk:abstract (dbk:abstract) = dbk:abstract * + + dbk:address (dbk:address) = dbk:address * + + dbk:annotation (dbk:annotation) = dbk:annotation * + + dbk:artpagenums (dbk:artpagenums) = dbk:artpagenums * + + dbk:author (dbk:author) = dbk:author * + + dbk:authorgroup (dbk:authorgroup) = dbk:authorgroup * + + dbk:authorinitials (dbk:authorinitials) = dbk:authorinitials * + + dbk:bibliocoverage (dbk:bibliocoverage) = dbk:bibliocoverage * + + dbk:biblioid (dbk:biblioid) = dbk:biblioid * + + dbk:bibliomisc (dbk:bibliomisc) = dbk:bibliomisc * + + dbk:bibliomset (dbk:bibliomset) = dbk:bibliomset * + + dbk:bibliorelation (dbk:bibliorelation) = dbk:bibliorelation * + + dbk:biblioset (dbk:biblioset) = dbk:biblioset * + + dbk:bibliosource (dbk:bibliosource) = dbk:bibliosource * + + dbk:citebiblioid (dbk:citebiblioid) = dbk:citebiblioid * + + dbk:citerefentry (dbk:citerefentry) = dbk:citerefentry * + + dbk:citetitle (dbk:citetitle) = dbk:citetitle * + + dbk:collab (dbk:collab) = dbk:collab * + + dbk:confgroup (dbk:confgroup) = dbk:confgroup * + + dbk:contractnum (dbk:contractnum) = dbk:contractnum * + + dbk:contractsponsor (dbk:contractsponsor) = dbk:contractsponsor * + + dbk:copyright (dbk:copyright) = dbk:copyright * + + dbk:cover (dbk:cover) = dbk:cover * + + dbk:edition (dbk:edition) = dbk:edition * + + dbk:editor (dbk:editor) = dbk:editor * + + dbk:extendedlink (dbk:extendedlink) = dbk:extendedlink * + + dbk:issuenum (dbk:issuenum) = dbk:issuenum * + + dbk:itermset (dbk:itermset) = dbk:itermset * + + dbk:keywordset (dbk:keywordset) = dbk:keywordset * + + dbk:legalnotice (dbk:legalnotice) = dbk:legalnotice * + + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject * + + dbk:org (dbk:org) = dbk:org * + + dbk:orgname (dbk:orgname) = dbk:orgname * + + dbk:othercredit (dbk:othercredit) = dbk:othercredit * + + dbk:pagenums (dbk:pagenums) = dbk:pagenums * + + dbk:person (dbk:person) = dbk:person * + + dbk:personblurb (dbk:personblurb) = dbk:personblurb * + + dbk:personname (dbk:personname) = dbk:personname * + + dbk:phrase (dbk:phrase) = dbk:phrase * + + dbk:printhistory (dbk:printhistory) = dbk:printhistory * + + dbk:productname (dbk:productname) = dbk:productname * + + dbk:productnumber (dbk:productnumber) = dbk:productnumber * + + dbk:pubdate (dbk:pubdate) = dbk:pubdate * + + dbk:publisher (dbk:publisher) = dbk:publisher * + + dbk:publishername (dbk:publishername) = dbk:publishername * + + dbk:releaseinfo (dbk:releaseinfo) = dbk:releaseinfo * + + dbk:revhistory (dbk:revhistory) = dbk:revhistory * + + dbk:seriesvolnums (dbk:seriesvolnums) = dbk:seriesvolnums * + + dbk:subjectset (dbk:subjectset) = dbk:subjectset * + + dbk:subscript (dbk:subscript) = dbk:subscript * + + dbk:subtitle (dbk:subtitle) = dbk:subtitle * + + dbk:superscript (dbk:superscript) = dbk:superscript * + + dbk:title (dbk:title) = dbk:title * + + dbk:titleabbrev (dbk:titleabbrev) = dbk:titleabbrev * + + dbk:volumenum (dbk:volumenum) = dbk:volumenum * + + jcr:xmltext (dbk:xmltext) = dbk:xmltext * + +[dbk:bibliomset] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:publishingInlines, argeodbk:ubiquitousInlines + + dbk:abstract (dbk:abstract) = dbk:abstract * + + dbk:address (dbk:address) = dbk:address * + + dbk:artpagenums (dbk:artpagenums) = dbk:artpagenums * + + dbk:author (dbk:author) = dbk:author * + + dbk:authorgroup (dbk:authorgroup) = dbk:authorgroup * + + dbk:authorinitials (dbk:authorinitials) = dbk:authorinitials * + + dbk:bibliocoverage (dbk:bibliocoverage) = dbk:bibliocoverage * + + dbk:biblioid (dbk:biblioid) = dbk:biblioid * + + dbk:bibliomisc (dbk:bibliomisc) = dbk:bibliomisc * + + dbk:bibliomset (dbk:bibliomset) = dbk:bibliomset * + + dbk:bibliorelation (dbk:bibliorelation) = dbk:bibliorelation * + + dbk:biblioset (dbk:biblioset) = dbk:biblioset * + + dbk:bibliosource (dbk:bibliosource) = dbk:bibliosource * + + dbk:citebiblioid (dbk:citebiblioid) = dbk:citebiblioid * + + dbk:citerefentry (dbk:citerefentry) = dbk:citerefentry * + + dbk:citetitle (dbk:citetitle) = dbk:citetitle * + + dbk:collab (dbk:collab) = dbk:collab * + + dbk:confgroup (dbk:confgroup) = dbk:confgroup * + + dbk:contractnum (dbk:contractnum) = dbk:contractnum * + + dbk:contractsponsor (dbk:contractsponsor) = dbk:contractsponsor * + + dbk:copyright (dbk:copyright) = dbk:copyright * + + dbk:cover (dbk:cover) = dbk:cover * + + dbk:edition (dbk:edition) = dbk:edition * + + dbk:editor (dbk:editor) = dbk:editor * + + dbk:extendedlink (dbk:extendedlink) = dbk:extendedlink * + + dbk:issuenum (dbk:issuenum) = dbk:issuenum * + + dbk:itermset (dbk:itermset) = dbk:itermset * + + dbk:keywordset (dbk:keywordset) = dbk:keywordset * + + dbk:legalnotice (dbk:legalnotice) = dbk:legalnotice * + + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject * + + dbk:org (dbk:org) = dbk:org * + + dbk:orgname (dbk:orgname) = dbk:orgname * + + dbk:othercredit (dbk:othercredit) = dbk:othercredit * + + dbk:pagenums (dbk:pagenums) = dbk:pagenums * + + dbk:person (dbk:person) = dbk:person * + + dbk:personblurb (dbk:personblurb) = dbk:personblurb * + + dbk:personname (dbk:personname) = dbk:personname * + + dbk:printhistory (dbk:printhistory) = dbk:printhistory * + + dbk:productname (dbk:productname) = dbk:productname * + + dbk:productnumber (dbk:productnumber) = dbk:productnumber * + + dbk:pubdate (dbk:pubdate) = dbk:pubdate * + + dbk:publisher (dbk:publisher) = dbk:publisher * + + dbk:publishername (dbk:publishername) = dbk:publishername * + + dbk:releaseinfo (dbk:releaseinfo) = dbk:releaseinfo * + + dbk:revhistory (dbk:revhistory) = dbk:revhistory * + + dbk:seriesvolnums (dbk:seriesvolnums) = dbk:seriesvolnums * + + dbk:subjectset (dbk:subjectset) = dbk:subjectset * + + dbk:subtitle (dbk:subtitle) = dbk:subtitle * + + dbk:title (dbk:title) = dbk:title * + + dbk:titleabbrev (dbk:titleabbrev) = dbk:titleabbrev * + + dbk:volumenum (dbk:volumenum) = dbk:volumenum * + - dbk:relation (String) + +[dbk:biblioref] > argeodbk:base, argeodbk:linkingAttributes + - dbk:begin (String) + - dbk:end (String) + - dbk:endterm (Reference) + - dbk:units (String) + - dbk:xrefstyle (String) + +[dbk:bibliorelation] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines + - dbk:class (String) + - dbk:otherclass (String) + - dbk:othertype (String) + - dbk:type (String) + +[dbk:biblioset] > argeodbk:base, argeodbk:linkingAttributes, argeodbk:publishingInlines + + dbk:abstract (dbk:abstract) = dbk:abstract * + + dbk:address (dbk:address) = dbk:address * + + dbk:annotation (dbk:annotation) = dbk:annotation * + + dbk:artpagenums (dbk:artpagenums) = dbk:artpagenums * + + dbk:author (dbk:author) = dbk:author * + + dbk:authorgroup (dbk:authorgroup) = dbk:authorgroup * + + dbk:authorinitials (dbk:authorinitials) = dbk:authorinitials * + + dbk:bibliocoverage (dbk:bibliocoverage) = dbk:bibliocoverage * + + dbk:biblioid (dbk:biblioid) = dbk:biblioid * + + dbk:bibliomisc (dbk:bibliomisc) = dbk:bibliomisc * + + dbk:bibliomset (dbk:bibliomset) = dbk:bibliomset * + + dbk:bibliorelation (dbk:bibliorelation) = dbk:bibliorelation * + + dbk:biblioset (dbk:biblioset) = dbk:biblioset * + + dbk:bibliosource (dbk:bibliosource) = dbk:bibliosource * + + dbk:citebiblioid (dbk:citebiblioid) = dbk:citebiblioid * + + dbk:citerefentry (dbk:citerefentry) = dbk:citerefentry * + + dbk:citetitle (dbk:citetitle) = dbk:citetitle * + + dbk:collab (dbk:collab) = dbk:collab * + + dbk:confgroup (dbk:confgroup) = dbk:confgroup * + + dbk:contractnum (dbk:contractnum) = dbk:contractnum * + + dbk:contractsponsor (dbk:contractsponsor) = dbk:contractsponsor * + + dbk:copyright (dbk:copyright) = dbk:copyright * + + dbk:cover (dbk:cover) = dbk:cover * + + dbk:edition (dbk:edition) = dbk:edition * + + dbk:editor (dbk:editor) = dbk:editor * + + dbk:extendedlink (dbk:extendedlink) = dbk:extendedlink * + + dbk:issuenum (dbk:issuenum) = dbk:issuenum * + + dbk:itermset (dbk:itermset) = dbk:itermset * + + dbk:keywordset (dbk:keywordset) = dbk:keywordset * + + dbk:legalnotice (dbk:legalnotice) = dbk:legalnotice * + + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject * + + dbk:org (dbk:org) = dbk:org * + + dbk:orgname (dbk:orgname) = dbk:orgname * + + dbk:othercredit (dbk:othercredit) = dbk:othercredit * + + dbk:pagenums (dbk:pagenums) = dbk:pagenums * + + dbk:person (dbk:person) = dbk:person * + + dbk:personblurb (dbk:personblurb) = dbk:personblurb * + + dbk:personname (dbk:personname) = dbk:personname * + + dbk:phrase (dbk:phrase) = dbk:phrase * + + dbk:printhistory (dbk:printhistory) = dbk:printhistory * + + dbk:productname (dbk:productname) = dbk:productname * + + dbk:productnumber (dbk:productnumber) = dbk:productnumber * + + dbk:pubdate (dbk:pubdate) = dbk:pubdate * + + dbk:publisher (dbk:publisher) = dbk:publisher * + + dbk:publishername (dbk:publishername) = dbk:publishername * + + dbk:releaseinfo (dbk:releaseinfo) = dbk:releaseinfo * + + dbk:revhistory (dbk:revhistory) = dbk:revhistory * + + dbk:seriesvolnums (dbk:seriesvolnums) = dbk:seriesvolnums * + + dbk:subjectset (dbk:subjectset) = dbk:subjectset * + + dbk:subscript (dbk:subscript) = dbk:subscript * + + dbk:subtitle (dbk:subtitle) = dbk:subtitle * + + dbk:superscript (dbk:superscript) = dbk:superscript * + + dbk:title (dbk:title) = dbk:title * + + dbk:titleabbrev (dbk:titleabbrev) = dbk:titleabbrev * + + dbk:volumenum (dbk:volumenum) = dbk:volumenum * + - dbk:relation (String) + +[dbk:bibliosource] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines + - dbk:class (String) + - dbk:otherclass (String) + +[dbk:blockquote] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled + + dbk:anchor (dbk:anchor) = dbk:anchor * + + dbk:annotation (dbk:annotation) = dbk:annotation * + + dbk:attribution (dbk:attribution) = dbk:attribution + + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead * + + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject * + + dbk:remark (dbk:remark) = dbk:remark * + + dbk:revhistory (dbk:revhistory) = dbk:revhistory * + + dbk:screenshot (dbk:screenshot) = dbk:screenshot * + +[dbk:book] > argeodbk:base, argeodbk:linkingAttributes, argeodbk:titled + + dbk:acknowledgements (dbk:acknowledgements) = dbk:acknowledgements * + + dbk:appendix (dbk:appendix) = dbk:appendix * + + dbk:article (dbk:article) = dbk:article * + + dbk:bibliography (dbk:bibliography) = dbk:bibliography * + + dbk:chapter (dbk:chapter) = dbk:chapter * + + dbk:colophon (dbk:colophon) = dbk:colophon * + + dbk:dedication (dbk:dedication) = dbk:dedication * + + dbk:glossary (dbk:glossary) = dbk:glossary * + + dbk:index (dbk:index) = dbk:index * + + dbk:part (dbk:part) = dbk:part * + + dbk:preface (dbk:preface) = dbk:preface * + + dbk:reference (dbk:reference) = dbk:reference * + + dbk:subtitle (dbk:subtitle) = dbk:subtitle * + + dbk:toc (dbk:toc) = dbk:toc * + - dbk:label (String) + - dbk:status (String) + +[dbk:bridgehead] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:markupInlines, argeodbk:publishingInlines, argeodbk:techDocInlines, argeodbk:ubiquitousInlines + - dbk:otherrenderas (String) + - dbk:renderas (String) + +[dbk:callout] > argeodbk:base, argeodbk:indexingInlines, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements + + dbk:anchor (dbk:anchor) = dbk:anchor * + + dbk:annotation (dbk:annotation) = dbk:annotation * + + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead * + + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject * + + dbk:remark (dbk:remark) = dbk:remark * + + dbk:revhistory (dbk:revhistory) = dbk:revhistory * + + dbk:screenshot (dbk:screenshot) = dbk:screenshot * + - dbk:arearefs (String) + +[dbk:calloutlist] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled + + dbk:anchor (dbk:anchor) = dbk:anchor * + + dbk:annotation (dbk:annotation) = dbk:annotation * + + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead * + + dbk:callout (dbk:callout) = dbk:callout * + + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject * + + dbk:remark (dbk:remark) = dbk:remark * + + dbk:revhistory (dbk:revhistory) = dbk:revhistory * + + dbk:screenshot (dbk:screenshot) = dbk:screenshot * + +[dbk:caption] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements + + dbk:anchor (dbk:anchor) = dbk:anchor * + + dbk:annotation (dbk:annotation) = dbk:annotation * + + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead * + + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject * + + dbk:remark (dbk:remark) = dbk:remark * + + dbk:revhistory (dbk:revhistory) = dbk:revhistory * + + dbk:screenshot (dbk:screenshot) = dbk:screenshot * + + jcr:xmltext (dbk:xmltext) = dbk:xmltext * + - dbk:class (String) + - dbk:lang (String) + - dbk:onclick (String) + - dbk:ondblclick (String) + - dbk:onkeydown (String) + - dbk:onkeypress (String) + - dbk:onkeyup (String) + - dbk:onmousedown (String) + - dbk:onmousemove (String) + - dbk:onmouseout (String) + - dbk:onmouseover (String) + - dbk:onmouseup (String) + - dbk:style (String) + - dbk:title (String) + +[dbk:caution] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled + + dbk:anchor (dbk:anchor) = dbk:anchor * + + dbk:annotation (dbk:annotation) = dbk:annotation * + + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead * + + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject * + + dbk:remark (dbk:remark) = dbk:remark * + + dbk:revhistory (dbk:revhistory) = dbk:revhistory * + + dbk:screenshot (dbk:screenshot) = dbk:screenshot * + +[dbk:chapter] > argeodbk:abstractSection, argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled + + dbk:anchor (dbk:anchor) = dbk:anchor * + + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject * + + dbk:refentry (dbk:refentry) = dbk:refentry * + + dbk:screenshot (dbk:screenshot) = dbk:screenshot * + + dbk:sect1 (dbk:sect1) = dbk:sect1 * + + dbk:section (dbk:section) = dbk:section * + + dbk:simplesect (dbk:simplesect) = dbk:simplesect * + + dbk:simplesect (dbk:simplesect) = dbk:simplesect * + +[dbk:citation] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:markupInlines, argeodbk:publishingInlines, argeodbk:techDocInlines, argeodbk:ubiquitousInlines + +[dbk:citebiblioid] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines + - dbk:class (String) + - dbk:otherclass (String) + +[dbk:citerefentry] > argeodbk:base, argeodbk:linkingAttributes + + dbk:manvolnum (dbk:manvolnum) = dbk:manvolnum + + dbk:refentrytitle (dbk:refentrytitle) = dbk:refentrytitle + +[dbk:citetitle] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:markupInlines, argeodbk:publishingInlines, argeodbk:techDocInlines, argeodbk:ubiquitousInlines + - dbk:pubwork (String) + +[dbk:city] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines + +[dbk:classname] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines + +[dbk:classsynopsis] > argeodbk:base, argeodbk:linkingAttributes + + dbk:classsynopsisinfo (dbk:classsynopsisinfo) = dbk:classsynopsisinfo * + + dbk:constructorsynopsis (dbk:constructorsynopsis) = dbk:constructorsynopsis * + + dbk:destructorsynopsis (dbk:destructorsynopsis) = dbk:destructorsynopsis * + + dbk:fieldsynopsis (dbk:fieldsynopsis) = dbk:fieldsynopsis * + + dbk:methodsynopsis (dbk:methodsynopsis) = dbk:methodsynopsis * + + dbk:ooclass (dbk:ooclass) = dbk:ooclass * + + dbk:ooexception (dbk:ooexception) = dbk:ooexception * + + dbk:oointerface (dbk:oointerface) = dbk:oointerface * + - dbk:class (String) + - dbk:language (String) + +[dbk:classsynopsisinfo] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:markupInlines, argeodbk:publishingInlines, argeodbk:techDocInlines, argeodbk:ubiquitousInlines + + dbk:co (dbk:co) = dbk:co * + + dbk:info (dbk:info) = dbk:info * + + dbk:lineannotation (dbk:lineannotation) = dbk:lineannotation * + + dbk:textobject (dbk:textobject) = dbk:textobject * + - dbk:continuation (String) + - dbk:language (String) + - dbk:linenumbering (String) + - dbk:startinglinenumber (String) + - xml:space (String) + +[dbk:cmdsynopsis] > argeodbk:base, argeodbk:linkingAttributes + + dbk:arg (dbk:arg) = dbk:arg * + + dbk:command (dbk:command) = dbk:command * + + dbk:group (dbk:group) = dbk:group * + + dbk:info (dbk:info) = dbk:info + + dbk:sbr (dbk:sbr) = dbk:sbr * + + dbk:synopfragment (dbk:synopfragment) = dbk:synopfragment * + - dbk:cmdlength (String) + - dbk:label (String) + - dbk:sepchar (String) + +[dbk:co] > argeodbk:base + - dbk:label (String) + - dbk:linkends (String) + +[dbk:code] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines + + dbk:classname (dbk:classname) = dbk:classname * + + dbk:exceptionname (dbk:exceptionname) = dbk:exceptionname * + + dbk:function (dbk:function) = dbk:function * + + dbk:initializer (dbk:initializer) = dbk:initializer * + + dbk:interfacename (dbk:interfacename) = dbk:interfacename * + + dbk:methodname (dbk:methodname) = dbk:methodname * + + dbk:modifier (dbk:modifier) = dbk:modifier * + + dbk:ooclass (dbk:ooclass) = dbk:ooclass * + + dbk:ooexception (dbk:ooexception) = dbk:ooexception * + + dbk:oointerface (dbk:oointerface) = dbk:oointerface * + + dbk:parameter (dbk:parameter) = dbk:parameter * + + dbk:returnvalue (dbk:returnvalue) = dbk:returnvalue * + + dbk:type (dbk:type) = dbk:type * + + dbk:varname (dbk:varname) = dbk:varname * + - dbk:language (String) + +[dbk:col] > nt:base + - dbk:align (String) + - dbk:annotations (String) + - dbk:arch (String) + - dbk:audience (String) + - dbk:char (String) + - dbk:charoff (String) + - dbk:class (String) + - dbk:condition (String) + - dbk:conformance (String) + - dbk:dir (String) + - dbk:lang (String) + - dbk:onclick (String) + - dbk:ondblclick (String) + - dbk:onkeydown (String) + - dbk:onkeypress (String) + - dbk:onkeyup (String) + - dbk:onmousedown (String) + - dbk:onmousemove (String) + - dbk:onmouseout (String) + - dbk:onmouseover (String) + - dbk:onmouseup (String) + - dbk:os (String) + - dbk:remap (String) + - dbk:revision (String) + - dbk:revisionflag (String) + - dbk:security (String) + - dbk:span (String) + - dbk:style (String) + - dbk:title (String) + - dbk:userlevel (String) + - dbk:valign (String) + - dbk:vendor (String) + - dbk:version (String) + - dbk:width (String) + - dbk:wordsize (String) + - dbk:xreflabel (String) + - xml:base (String) + - xml:id (String) + - xml:lang (String) + +[dbk:colgroup] > nt:base + + dbk:col (dbk:col) = dbk:col * + - dbk:align (String) + - dbk:annotations (String) + - dbk:arch (String) + - dbk:audience (String) + - dbk:char (String) + - dbk:charoff (String) + - dbk:class (String) + - dbk:condition (String) + - dbk:conformance (String) + - dbk:dir (String) + - dbk:lang (String) + - dbk:onclick (String) + - dbk:ondblclick (String) + - dbk:onkeydown (String) + - dbk:onkeypress (String) + - dbk:onkeyup (String) + - dbk:onmousedown (String) + - dbk:onmousemove (String) + - dbk:onmouseout (String) + - dbk:onmouseover (String) + - dbk:onmouseup (String) + - dbk:os (String) + - dbk:remap (String) + - dbk:revision (String) + - dbk:revisionflag (String) + - dbk:security (String) + - dbk:span (String) + - dbk:style (String) + - dbk:title (String) + - dbk:userlevel (String) + - dbk:valign (String) + - dbk:vendor (String) + - dbk:version (String) + - dbk:width (String) + - dbk:wordsize (String) + - dbk:xreflabel (String) + - xml:base (String) + - xml:id (String) + - xml:lang (String) + +[dbk:collab] > argeodbk:base, argeodbk:linkingAttributes + + dbk:affiliation (dbk:affiliation) = dbk:affiliation * + + dbk:org (dbk:org) = dbk:org * + + dbk:orgname (dbk:orgname) = dbk:orgname * + + dbk:person (dbk:person) = dbk:person * + + dbk:personname (dbk:personname) = dbk:personname * + +[dbk:colophon] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled + + dbk:anchor (dbk:anchor) = dbk:anchor * + + dbk:annotation (dbk:annotation) = dbk:annotation * + + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead * + + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject * + + dbk:remark (dbk:remark) = dbk:remark * + + dbk:revhistory (dbk:revhistory) = dbk:revhistory * + + dbk:screenshot (dbk:screenshot) = dbk:screenshot * + + dbk:subtitle (dbk:subtitle) = dbk:subtitle * + - dbk:label (String) + - dbk:status (String) + +[dbk:colspec] > argeodbk:base, argeodbk:linkingAttributes + - dbk:align (String) + - dbk:char (String) + - dbk:charoff (String) + - dbk:colname (String) + - dbk:colnum (String) + - dbk:colsep (String) + - dbk:colwidth (String) + - dbk:rowsep (String) + +[dbk:command] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines + +[dbk:computeroutput] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:markupInlines, argeodbk:ubiquitousInlines + + dbk:co (dbk:co) = dbk:co * + + dbk:command (dbk:command) = dbk:command * + + dbk:computeroutput (dbk:computeroutput) = dbk:computeroutput * + + dbk:envar (dbk:envar) = dbk:envar * + + dbk:filename (dbk:filename) = dbk:filename * + + dbk:nonterminal (dbk:nonterminal) = dbk:nonterminal * + + dbk:option (dbk:option) = dbk:option * + + dbk:optional (dbk:optional) = dbk:optional * + + dbk:package (dbk:package) = dbk:package * + + dbk:parameter (dbk:parameter) = dbk:parameter * + + dbk:prompt (dbk:prompt) = dbk:prompt * + + dbk:property (dbk:property) = dbk:property * + + dbk:replaceable (dbk:replaceable) = dbk:replaceable * + + dbk:systemitem (dbk:systemitem) = dbk:systemitem * + + dbk:termdef (dbk:termdef) = dbk:termdef * + + dbk:userinput (dbk:userinput) = dbk:userinput * + + jcr:xmltext (dbk:xmltext) = dbk:xmltext * + +[dbk:confdates] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines + +[dbk:confgroup] > argeodbk:base, argeodbk:linkingAttributes + + dbk:address (dbk:address) = dbk:address * + + dbk:confdates (dbk:confdates) = dbk:confdates * + + dbk:confnum (dbk:confnum) = dbk:confnum * + + dbk:confsponsor (dbk:confsponsor) = dbk:confsponsor * + + dbk:conftitle (dbk:conftitle) = dbk:conftitle * + +[dbk:confnum] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines + +[dbk:confsponsor] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines + +[dbk:conftitle] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines + +[dbk:constant] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines + - dbk:class (String) + +[dbk:constraint] > argeodbk:base, argeodbk:linkingAttributes + +[dbk:constraintdef] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled + + dbk:anchor (dbk:anchor) = dbk:anchor * + + dbk:annotation (dbk:annotation) = dbk:annotation * + + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead * + + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject * + + dbk:remark (dbk:remark) = dbk:remark * + + dbk:revhistory (dbk:revhistory) = dbk:revhistory * + + dbk:screenshot (dbk:screenshot) = dbk:screenshot * + +[dbk:constructorsynopsis] > argeodbk:base, argeodbk:linkingAttributes + + dbk:exceptionname (dbk:exceptionname) = dbk:exceptionname * + + dbk:methodname (dbk:methodname) = dbk:methodname + + dbk:methodparam (dbk:methodparam) = dbk:methodparam * + + dbk:modifier (dbk:modifier) = dbk:modifier * + + dbk:void (dbk:void) = dbk:void + - dbk:language (String) + +[dbk:contractnum] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines + +[dbk:contractsponsor] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines + +[dbk:contrib] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines + +[dbk:copyright] > argeodbk:base, argeodbk:linkingAttributes + + dbk:holder (dbk:holder) = dbk:holder * + + dbk:year (dbk:year) = dbk:year * + +[dbk:coref] > argeodbk:base, argeodbk:linkingAttributes + - dbk:label (String) + +[dbk:country] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines + +[dbk:cover] > argeodbk:base, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements + + dbk:anchor (dbk:anchor) = dbk:anchor * + + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead * + + dbk:classsynopsis (dbk:classsynopsis) = dbk:classsynopsis * + + dbk:cmdsynopsis (dbk:cmdsynopsis) = dbk:cmdsynopsis * + + dbk:constraintdef (dbk:constraintdef) = dbk:constraintdef * + + dbk:constructorsynopsis (dbk:constructorsynopsis) = dbk:constructorsynopsis * + + dbk:destructorsynopsis (dbk:destructorsynopsis) = dbk:destructorsynopsis * + + dbk:fieldsynopsis (dbk:fieldsynopsis) = dbk:fieldsynopsis * + + dbk:funcsynopsis (dbk:funcsynopsis) = dbk:funcsynopsis * + + dbk:informalequation (dbk:informalequation) = dbk:informalequation * + + dbk:informalexample (dbk:informalexample) = dbk:informalexample * + + dbk:informalfigure (dbk:informalfigure) = dbk:informalfigure * + + dbk:informaltable (dbk:informaltable) = dbk:informaltable * + + dbk:literallayout (dbk:literallayout) = dbk:literallayout * + + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject * + + dbk:methodsynopsis (dbk:methodsynopsis) = dbk:methodsynopsis * + + dbk:msgset (dbk:msgset) = dbk:msgset * + + dbk:productionset (dbk:productionset) = dbk:productionset * + + dbk:programlisting (dbk:programlisting) = dbk:programlisting * + + dbk:programlistingco (dbk:programlistingco) = dbk:programlistingco * + + dbk:remark (dbk:remark) = dbk:remark * + + dbk:revhistory (dbk:revhistory) = dbk:revhistory * + + dbk:screen (dbk:screen) = dbk:screen * + + dbk:screenco (dbk:screenco) = dbk:screenco * + + dbk:screenshot (dbk:screenshot) = dbk:screenshot * + + dbk:synopsis (dbk:synopsis) = dbk:synopsis * + + dbk:task (dbk:task) = dbk:task * + +[dbk:database] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines + - dbk:class (String) + +[dbk:date] > argeodbk:base, argeodbk:linkingAttributes + + jcr:xmltext (dbk:xmltext) = dbk:xmltext * + +[dbk:dedication] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled + + dbk:anchor (dbk:anchor) = dbk:anchor * + + dbk:annotation (dbk:annotation) = dbk:annotation * + + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead * + + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject * + + dbk:remark (dbk:remark) = dbk:remark * + + dbk:revhistory (dbk:revhistory) = dbk:revhistory * + + dbk:screenshot (dbk:screenshot) = dbk:screenshot * + + dbk:subtitle (dbk:subtitle) = dbk:subtitle * + - dbk:label (String) + - dbk:status (String) + +[dbk:destructorsynopsis] > argeodbk:base, argeodbk:linkingAttributes + + dbk:exceptionname (dbk:exceptionname) = dbk:exceptionname * + + dbk:methodname (dbk:methodname) = dbk:methodname + + dbk:methodparam (dbk:methodparam) = dbk:methodparam * + + dbk:modifier (dbk:modifier) = dbk:modifier * + + dbk:void (dbk:void) = dbk:void + - dbk:language (String) + +[dbk:edition] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines + +[dbk:editor] > argeodbk:base, argeodbk:linkingAttributes + + dbk:address (dbk:address) = dbk:address * + + dbk:affiliation (dbk:affiliation) = dbk:affiliation * + + dbk:contrib (dbk:contrib) = dbk:contrib * + + dbk:email (dbk:email) = dbk:email * + + dbk:orgdiv (dbk:orgdiv) = dbk:orgdiv * + + dbk:orgname (dbk:orgname) = dbk:orgname + + dbk:personblurb (dbk:personblurb) = dbk:personblurb * + + dbk:personname (dbk:personname) = dbk:personname + + dbk:uri (dbk:uri) = dbk:uri * + +[dbk:email] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines + +[dbk:emphasis] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:markupInlines, argeodbk:publishingInlines, argeodbk:techDocInlines, argeodbk:ubiquitousInlines + +[dbk:entry] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:markupInlines, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:publishingInlines, argeodbk:techDocElements, argeodbk:techDocInlines, argeodbk:ubiquitousInlines + + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead * + + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject * + + dbk:revhistory (dbk:revhistory) = dbk:revhistory * + + dbk:screenshot (dbk:screenshot) = dbk:screenshot * + - dbk:align (String) + - dbk:char (String) + - dbk:charoff (String) + - dbk:colname (String) + - dbk:colsep (String) + - dbk:morerows (String) + - dbk:nameend (String) + - dbk:namest (String) + - dbk:rotate (String) + - dbk:rowsep (String) + - dbk:spanname (String) + - dbk:valign (String) + +[dbk:entrytbl] > argeodbk:base, argeodbk:linkingAttributes + + dbk:colspec (dbk:colspec) = dbk:colspec * + + dbk:spanspec (dbk:spanspec) = dbk:spanspec * + + dbk:tbody (dbk:tbody) = dbk:tbody + + dbk:thead (dbk:thead) = dbk:thead + - dbk:align (String) + - dbk:char (String) + - dbk:charoff (String) + - dbk:colname (String) + - dbk:cols (String) + - dbk:colsep (String) + - dbk:nameend (String) + - dbk:namest (String) + - dbk:rowsep (String) + - dbk:spanname (String) + - dbk:tgroupstyle (String) + +[dbk:envar] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines + +[dbk:epigraph] > argeodbk:base, argeodbk:linkingAttributes, argeodbk:paragraphElements + + dbk:anchor (dbk:anchor) = dbk:anchor * + + dbk:attribution (dbk:attribution) = dbk:attribution + + dbk:info (dbk:info) = dbk:info + + dbk:literallayout (dbk:literallayout) = dbk:literallayout * + +[dbk:equation] > argeodbk:base, argeodbk:linkingAttributes, argeodbk:titled + + dbk:alt (dbk:alt) = dbk:alt + + dbk:caption (dbk:caption) = dbk:caption + + dbk:mathphrase (dbk:mathphrase) = dbk:mathphrase * + + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject * + - dbk:floatstyle (String) + - dbk:label (String) + - dbk:pgwide (String) + +[dbk:errorcode] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines + +[dbk:errorname] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines + +[dbk:errortext] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines + +[dbk:errortype] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines + +[dbk:example] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled + + dbk:anchor (dbk:anchor) = dbk:anchor * + + dbk:annotation (dbk:annotation) = dbk:annotation * + + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead * + + dbk:caption (dbk:caption) = dbk:caption + + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject * + + dbk:remark (dbk:remark) = dbk:remark * + + dbk:revhistory (dbk:revhistory) = dbk:revhistory * + + dbk:screenshot (dbk:screenshot) = dbk:screenshot * + - dbk:floatstyle (String) + - dbk:label (String) + - dbk:pgwide (String) + - dbk:width (String) + +[dbk:exceptionname] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines + +[dbk:extendedlink] > argeodbk:base + + dbk:arc (dbk:arc) = dbk:arc * + + dbk:locator (dbk:locator) = dbk:locator * + +[dbk:fax] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines + +[dbk:fieldsynopsis] > argeodbk:base, argeodbk:linkingAttributes + + dbk:initializer (dbk:initializer) = dbk:initializer + + dbk:modifier (dbk:modifier) = dbk:modifier * + + dbk:type (dbk:type) = dbk:type + + dbk:varname (dbk:varname) = dbk:varname + - dbk:language (String) + +[dbk:figure] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled + + dbk:anchor (dbk:anchor) = dbk:anchor * + + dbk:annotation (dbk:annotation) = dbk:annotation * + + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead * + + dbk:caption (dbk:caption) = dbk:caption + + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject * + + dbk:remark (dbk:remark) = dbk:remark * + + dbk:revhistory (dbk:revhistory) = dbk:revhistory * + + dbk:screenshot (dbk:screenshot) = dbk:screenshot * + - dbk:floatstyle (String) + - dbk:label (String) + - dbk:pgwide (String) + +[dbk:filename] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines + - dbk:class (String) + - dbk:path (String) + +[dbk:firstname] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines + +[dbk:firstterm] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:markupInlines, argeodbk:publishingInlines, argeodbk:techDocInlines, argeodbk:ubiquitousInlines + - dbk:baseform (String) + +[dbk:footnote] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements + + dbk:anchor (dbk:anchor) = dbk:anchor * + + dbk:annotation (dbk:annotation) = dbk:annotation * + + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead * + + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject * + + dbk:remark (dbk:remark) = dbk:remark * + + dbk:revhistory (dbk:revhistory) = dbk:revhistory * + + dbk:screenshot (dbk:screenshot) = dbk:screenshot * + - dbk:label (String) + +[dbk:footnoteref] > argeodbk:base, argeodbk:linkingAttributes + - dbk:label (String) + +[dbk:foreignphrase] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:publishingInlines + + dbk:anchor (dbk:anchor) = dbk:anchor * + + dbk:application (dbk:application) = dbk:application * + + dbk:biblioref (dbk:biblioref) = dbk:biblioref * + + dbk:database (dbk:database) = dbk:database * + + dbk:hardware (dbk:hardware) = dbk:hardware * + + dbk:inlinemediaobject (dbk:inlinemediaobject) = dbk:inlinemediaobject * + + dbk:link (dbk:link) = dbk:link * + + dbk:olink (dbk:olink) = dbk:olink * + + dbk:phrase (dbk:phrase) = dbk:phrase * + + dbk:productname (dbk:productname) = dbk:productname * + + dbk:productnumber (dbk:productnumber) = dbk:productnumber * + + dbk:subscript (dbk:subscript) = dbk:subscript * + + dbk:superscript (dbk:superscript) = dbk:superscript * + + dbk:trademark (dbk:trademark) = dbk:trademark * + + dbk:xref (dbk:xref) = dbk:xref * + + jcr:xmltext (dbk:xmltext) = dbk:xmltext * + +[dbk:formalpara] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:titled + + dbk:para (dbk:para) = dbk:para + +[dbk:funcdef] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines + + dbk:function (dbk:function) = dbk:function * + + dbk:type (dbk:type) = dbk:type * + +[dbk:funcparams] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines + +[dbk:funcprototype] > argeodbk:base, argeodbk:linkingAttributes + + dbk:funcdef (dbk:funcdef) = dbk:funcdef + + dbk:modifier (dbk:modifier) = dbk:modifier * + + dbk:paramdef (dbk:paramdef) = dbk:paramdef * + + dbk:varargs (dbk:varargs) = dbk:varargs + + dbk:varargs (dbk:varargs) = dbk:varargs + + dbk:void (dbk:void) = dbk:void + +[dbk:funcsynopsis] > argeodbk:base, argeodbk:linkingAttributes + + dbk:funcprototype (dbk:funcprototype) = dbk:funcprototype * + + dbk:funcsynopsisinfo (dbk:funcsynopsisinfo) = dbk:funcsynopsisinfo * + + dbk:info (dbk:info) = dbk:info + - dbk:language (String) + +[dbk:funcsynopsisinfo] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:markupInlines, argeodbk:publishingInlines, argeodbk:techDocInlines, argeodbk:ubiquitousInlines + + dbk:co (dbk:co) = dbk:co * + + dbk:info (dbk:info) = dbk:info * + + dbk:lineannotation (dbk:lineannotation) = dbk:lineannotation * + + dbk:textobject (dbk:textobject) = dbk:textobject * + - dbk:continuation (String) + - dbk:language (String) + - dbk:linenumbering (String) + - dbk:startinglinenumber (String) + - xml:space (String) + +[dbk:function] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines + +[dbk:glossary] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled + + dbk:anchor (dbk:anchor) = dbk:anchor * + + dbk:annotation (dbk:annotation) = dbk:annotation * + + dbk:bibliography (dbk:bibliography) = dbk:bibliography + + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead * + + dbk:glossdiv (dbk:glossdiv) = dbk:glossdiv * + + dbk:glossentry (dbk:glossentry) = dbk:glossentry * + + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject * + + dbk:remark (dbk:remark) = dbk:remark * + + dbk:revhistory (dbk:revhistory) = dbk:revhistory * + + dbk:screenshot (dbk:screenshot) = dbk:screenshot * + + dbk:subtitle (dbk:subtitle) = dbk:subtitle * + - dbk:label (String) + - dbk:status (String) + +[dbk:glossdef] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements + + dbk:anchor (dbk:anchor) = dbk:anchor * + + dbk:annotation (dbk:annotation) = dbk:annotation * + + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead * + + dbk:glossseealso (dbk:glossseealso) = dbk:glossseealso * + + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject * + + dbk:remark (dbk:remark) = dbk:remark * + + dbk:revhistory (dbk:revhistory) = dbk:revhistory * + + dbk:screenshot (dbk:screenshot) = dbk:screenshot * + - dbk:subject (String) + +[dbk:glossdiv] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled + + dbk:anchor (dbk:anchor) = dbk:anchor * + + dbk:annotation (dbk:annotation) = dbk:annotation * + + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead * + + dbk:glossentry (dbk:glossentry) = dbk:glossentry * + + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject * + + dbk:remark (dbk:remark) = dbk:remark * + + dbk:revhistory (dbk:revhistory) = dbk:revhistory * + + dbk:screenshot (dbk:screenshot) = dbk:screenshot * + + dbk:subtitle (dbk:subtitle) = dbk:subtitle * + - dbk:label (String) + - dbk:status (String) + +[dbk:glossentry] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes + + dbk:abbrev (dbk:abbrev) = dbk:abbrev + + dbk:acronym (dbk:acronym) = dbk:acronym + + dbk:glossdef (dbk:glossdef) = dbk:glossdef * + + dbk:glosssee (dbk:glosssee) = dbk:glosssee + + dbk:glossterm (dbk:glossterm) = dbk:glossterm + - dbk:sortas (String) + +[dbk:glosslist] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled + + dbk:anchor (dbk:anchor) = dbk:anchor * + + dbk:annotation (dbk:annotation) = dbk:annotation * + + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead * + + dbk:glossentry (dbk:glossentry) = dbk:glossentry * + + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject * + + dbk:remark (dbk:remark) = dbk:remark * + + dbk:revhistory (dbk:revhistory) = dbk:revhistory * + + dbk:screenshot (dbk:screenshot) = dbk:screenshot * + +[dbk:glosssee] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:markupInlines, argeodbk:publishingInlines, argeodbk:techDocInlines, argeodbk:ubiquitousInlines + - dbk:otherterm (Reference) + +[dbk:glossseealso] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:markupInlines, argeodbk:publishingInlines, argeodbk:techDocInlines, argeodbk:ubiquitousInlines + - dbk:otherterm (Reference) + +[dbk:glossterm] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:markupInlines, argeodbk:publishingInlines, argeodbk:techDocInlines, argeodbk:ubiquitousInlines + - dbk:baseform (String) + +[dbk:group] > argeodbk:base, argeodbk:linkingAttributes + + dbk:arg (dbk:arg) = dbk:arg * + + dbk:group (dbk:group) = dbk:group * + + dbk:option (dbk:option) = dbk:option * + + dbk:replaceable (dbk:replaceable) = dbk:replaceable * + + dbk:sbr (dbk:sbr) = dbk:sbr * + + dbk:synopfragmentref (dbk:synopfragmentref) = dbk:synopfragmentref * + - dbk:choice (String) + - dbk:rep (String) + +[dbk:guibutton] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines + + dbk:accel (dbk:accel) = dbk:accel * + +[dbk:guiicon] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines + + dbk:accel (dbk:accel) = dbk:accel * + +[dbk:guilabel] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines + + dbk:accel (dbk:accel) = dbk:accel * + +[dbk:guimenu] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines + + dbk:accel (dbk:accel) = dbk:accel * + +[dbk:guimenuitem] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines + + dbk:accel (dbk:accel) = dbk:accel * + +[dbk:guisubmenu] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines + + dbk:accel (dbk:accel) = dbk:accel * + +[dbk:hardware] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines + +[dbk:holder] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines + +[dbk:honorific] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines + +[dbk:imagedata] > argeodbk:base + + dbk:info (dbk:info) = dbk:info + - dbk:align (String) + - dbk:contentdepth (String) + - dbk:contentwidth (String) + - dbk:depth (String) + - dbk:entityref (String) + - dbk:fileref (String) + - dbk:format (String) + - dbk:scale (String) + - dbk:scalefit (String) + - dbk:valign (String) + - dbk:width (String) + +[dbk:imageobject] > argeodbk:base, argeodbk:linkingAttributes + + dbk:imagedata (dbk:imagedata) = dbk:imagedata + + dbk:info (dbk:info) = dbk:info + +[dbk:imageobjectco] > argeodbk:base, argeodbk:linkingAttributes + + dbk:areaspec (dbk:areaspec) = dbk:areaspec + + dbk:calloutlist (dbk:calloutlist) = dbk:calloutlist * + + dbk:imageobject (dbk:imageobject) = dbk:imageobject * + + dbk:info (dbk:info) = dbk:info + +[dbk:important] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled + + dbk:anchor (dbk:anchor) = dbk:anchor * + + dbk:annotation (dbk:annotation) = dbk:annotation * + + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead * + + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject * + + dbk:remark (dbk:remark) = dbk:remark * + + dbk:revhistory (dbk:revhistory) = dbk:revhistory * + + dbk:screenshot (dbk:screenshot) = dbk:screenshot * + +[dbk:index] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled + + dbk:anchor (dbk:anchor) = dbk:anchor * + + dbk:annotation (dbk:annotation) = dbk:annotation * + + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead * + + dbk:indexdiv (dbk:indexdiv) = dbk:indexdiv * + + dbk:indexentry (dbk:indexentry) = dbk:indexentry * + + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject * + + dbk:remark (dbk:remark) = dbk:remark * + + dbk:revhistory (dbk:revhistory) = dbk:revhistory * + + dbk:screenshot (dbk:screenshot) = dbk:screenshot * + + dbk:subtitle (dbk:subtitle) = dbk:subtitle * + - dbk:label (String) + - dbk:status (String) + - dbk:type (String) + +[dbk:indexdiv] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled + + dbk:anchor (dbk:anchor) = dbk:anchor * + + dbk:annotation (dbk:annotation) = dbk:annotation * + + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead * + + dbk:indexentry (dbk:indexentry) = dbk:indexentry * + + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject * + + dbk:remark (dbk:remark) = dbk:remark * + + dbk:revhistory (dbk:revhistory) = dbk:revhistory * + + dbk:screenshot (dbk:screenshot) = dbk:screenshot * + + dbk:subtitle (dbk:subtitle) = dbk:subtitle * + - dbk:label (String) + - dbk:status (String) + +[dbk:indexentry] > argeodbk:base, argeodbk:linkingAttributes + + dbk:primaryie (dbk:primaryie) = dbk:primaryie + + dbk:secondaryie (dbk:secondaryie) = dbk:secondaryie * + + dbk:seealsoie (dbk:seealsoie) = dbk:seealsoie * + + dbk:seeie (dbk:seeie) = dbk:seeie * + + dbk:tertiaryie (dbk:tertiaryie) = dbk:tertiaryie * + +[dbk:indexterm] > argeodbk:base, argeodbk:linkingAttributes + + dbk:primary (dbk:primary) = dbk:primary + + dbk:secondary (dbk:secondary) = dbk:secondary + + dbk:see (dbk:see) = dbk:see + + dbk:seealso (dbk:seealso) = dbk:seealso * + + dbk:tertiary (dbk:tertiary) = dbk:tertiary + - dbk:class (String) + - dbk:pagenum (String) + - dbk:scope (String) + - dbk:significance (String) + - dbk:startref (Reference) + - dbk:type (String) + - dbk:zone (String) + +[dbk:info] > argeodbk:base + + dbk:abstract (dbk:abstract) = dbk:abstract * + + dbk:address (dbk:address) = dbk:address * + + dbk:annotation (dbk:annotation) = dbk:annotation * + + dbk:artpagenums (dbk:artpagenums) = dbk:artpagenums * + + dbk:author (dbk:author) = dbk:author * + + dbk:authorgroup (dbk:authorgroup) = dbk:authorgroup * + + dbk:authorinitials (dbk:authorinitials) = dbk:authorinitials * + + dbk:bibliocoverage (dbk:bibliocoverage) = dbk:bibliocoverage * + + dbk:biblioid (dbk:biblioid) = dbk:biblioid * + + dbk:bibliomisc (dbk:bibliomisc) = dbk:bibliomisc * + + dbk:bibliomset (dbk:bibliomset) = dbk:bibliomset * + + dbk:bibliorelation (dbk:bibliorelation) = dbk:bibliorelation * + + dbk:biblioset (dbk:biblioset) = dbk:biblioset * + + dbk:bibliosource (dbk:bibliosource) = dbk:bibliosource * + + dbk:collab (dbk:collab) = dbk:collab * + + dbk:confgroup (dbk:confgroup) = dbk:confgroup * + + dbk:contractnum (dbk:contractnum) = dbk:contractnum * + + dbk:contractsponsor (dbk:contractsponsor) = dbk:contractsponsor * + + dbk:copyright (dbk:copyright) = dbk:copyright * + + dbk:cover (dbk:cover) = dbk:cover * + + dbk:date (dbk:date) = dbk:date * + + dbk:edition (dbk:edition) = dbk:edition * + + dbk:editor (dbk:editor) = dbk:editor * + + dbk:extendedlink (dbk:extendedlink) = dbk:extendedlink * + + dbk:issuenum (dbk:issuenum) = dbk:issuenum * + + dbk:itermset (dbk:itermset) = dbk:itermset * + + dbk:keywordset (dbk:keywordset) = dbk:keywordset * + + dbk:legalnotice (dbk:legalnotice) = dbk:legalnotice * + + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject * + + dbk:org (dbk:org) = dbk:org * + + dbk:orgname (dbk:orgname) = dbk:orgname * + + dbk:othercredit (dbk:othercredit) = dbk:othercredit * + + dbk:pagenums (dbk:pagenums) = dbk:pagenums * + + dbk:printhistory (dbk:printhistory) = dbk:printhistory * + + dbk:productname (dbk:productname) = dbk:productname * + + dbk:productnumber (dbk:productnumber) = dbk:productnumber * + + dbk:pubdate (dbk:pubdate) = dbk:pubdate * + + dbk:publisher (dbk:publisher) = dbk:publisher * + + dbk:publishername (dbk:publishername) = dbk:publishername * + + dbk:releaseinfo (dbk:releaseinfo) = dbk:releaseinfo * + + dbk:revhistory (dbk:revhistory) = dbk:revhistory * + + dbk:seriesvolnums (dbk:seriesvolnums) = dbk:seriesvolnums * + + dbk:subjectset (dbk:subjectset) = dbk:subjectset * + + dbk:subtitle (dbk:subtitle) = dbk:subtitle * + + dbk:title (dbk:title) = dbk:title * + + dbk:titleabbrev (dbk:titleabbrev) = dbk:titleabbrev * + + dbk:volumenum (dbk:volumenum) = dbk:volumenum * + +[dbk:informalequation] > argeodbk:base, argeodbk:linkingAttributes + + dbk:alt (dbk:alt) = dbk:alt + + dbk:caption (dbk:caption) = dbk:caption + + dbk:info (dbk:info) = dbk:info + + dbk:mathphrase (dbk:mathphrase) = dbk:mathphrase * + + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject * + +[dbk:informalexample] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements + + dbk:anchor (dbk:anchor) = dbk:anchor * + + dbk:annotation (dbk:annotation) = dbk:annotation * + + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead * + + dbk:caption (dbk:caption) = dbk:caption + + dbk:info (dbk:info) = dbk:info + + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject * + + dbk:remark (dbk:remark) = dbk:remark * + + dbk:revhistory (dbk:revhistory) = dbk:revhistory * + + dbk:screenshot (dbk:screenshot) = dbk:screenshot * + - dbk:floatstyle (String) + - dbk:width (String) + +[dbk:informalfigure] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements + + dbk:anchor (dbk:anchor) = dbk:anchor * + + dbk:annotation (dbk:annotation) = dbk:annotation * + + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead * + + dbk:caption (dbk:caption) = dbk:caption + + dbk:info (dbk:info) = dbk:info + + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject * + + dbk:remark (dbk:remark) = dbk:remark * + + dbk:revhistory (dbk:revhistory) = dbk:revhistory * + + dbk:screenshot (dbk:screenshot) = dbk:screenshot * + - dbk:floatstyle (String) + - dbk:label (String) + - dbk:pgwide (String) + +[dbk:informaltable] > argeodbk:base, argeodbk:linkingAttributes + + dbk:col (dbk:col) = dbk:col * + + dbk:colgroup (dbk:colgroup) = dbk:colgroup * + + dbk:info (dbk:info) = dbk:info + + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject * + + dbk:tbody (dbk:tbody) = dbk:tbody * + + dbk:textobject (dbk:textobject) = dbk:textobject * + + dbk:tfoot (dbk:tfoot) = dbk:tfoot + + dbk:tgroup (dbk:tgroup) = dbk:tgroup * + + dbk:thead (dbk:thead) = dbk:thead + + dbk:tr (dbk:tr) = dbk:tr * + - dbk:border (String) + - dbk:cellpadding (String) + - dbk:cellspacing (String) + - dbk:class (String) + - dbk:colsep (String) + - dbk:floatstyle (String) + - dbk:frame (String) + - dbk:lang (String) + - dbk:onclick (String) + - dbk:ondblclick (String) + - dbk:onkeydown (String) + - dbk:onkeypress (String) + - dbk:onkeyup (String) + - dbk:onmousedown (String) + - dbk:onmousemove (String) + - dbk:onmouseout (String) + - dbk:onmouseover (String) + - dbk:onmouseup (String) + - dbk:orient (String) + - dbk:pgwide (String) + - dbk:rowheader (String) + - dbk:rowsep (String) + - dbk:rules (String) + - dbk:style (String) + - dbk:summary (String) + - dbk:tabstyle (String) + - dbk:title (String) + - dbk:width (String) + +[dbk:initializer] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines + +[dbk:inlineequation] > argeodbk:base, argeodbk:linkingAttributes + + dbk:alt (dbk:alt) = dbk:alt + + dbk:inlinemediaobject (dbk:inlinemediaobject) = dbk:inlinemediaobject * + + dbk:mathphrase (dbk:mathphrase) = dbk:mathphrase * + +[dbk:inlinemediaobject] > argeodbk:base, argeodbk:linkingAttributes + + dbk:alt (dbk:alt) = dbk:alt + + dbk:audioobject (dbk:audioobject) = dbk:audioobject * + + dbk:imageobject (dbk:imageobject) = dbk:imageobject * + + dbk:imageobjectco (dbk:imageobjectco) = dbk:imageobjectco * + + dbk:info (dbk:info) = dbk:info + + dbk:textobject (dbk:textobject) = dbk:textobject * + + dbk:videoobject (dbk:videoobject) = dbk:videoobject * + +[dbk:interfacename] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines + +[dbk:issuenum] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines + +[dbk:itemizedlist] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled + + dbk:anchor (dbk:anchor) = dbk:anchor * + + dbk:annotation (dbk:annotation) = dbk:annotation * + + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead * + + dbk:listitem (dbk:listitem) = dbk:listitem * + + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject * + + dbk:remark (dbk:remark) = dbk:remark * + + dbk:revhistory (dbk:revhistory) = dbk:revhistory * + + dbk:screenshot (dbk:screenshot) = dbk:screenshot * + - dbk:mark (String) + - dbk:spacing (String) + +[dbk:itermset] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes + +[dbk:jobtitle] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines + +[dbk:keycap] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines + - dbk:function (String) + - dbk:otherfunction (String) + +[dbk:keycode] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines + +[dbk:keycombo] > argeodbk:base, argeodbk:linkingAttributes + + dbk:keycap (dbk:keycap) = dbk:keycap * + + dbk:keycombo (dbk:keycombo) = dbk:keycombo * + + dbk:keysym (dbk:keysym) = dbk:keysym * + + dbk:mousebutton (dbk:mousebutton) = dbk:mousebutton * + - dbk:action (String) + - dbk:otheraction (String) + +[dbk:keysym] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines + +[dbk:keyword] > argeodbk:base, argeodbk:linkingAttributes + + jcr:xmltext (dbk:xmltext) = dbk:xmltext * + +[dbk:keywordset] > argeodbk:base, argeodbk:linkingAttributes + + dbk:keyword (dbk:keyword) = dbk:keyword * + +[dbk:label] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines + +[dbk:legalnotice] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled + + dbk:anchor (dbk:anchor) = dbk:anchor * + + dbk:annotation (dbk:annotation) = dbk:annotation * + + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead * + + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject * + + dbk:remark (dbk:remark) = dbk:remark * + + dbk:revhistory (dbk:revhistory) = dbk:revhistory * + + dbk:screenshot (dbk:screenshot) = dbk:screenshot * + +[dbk:lhs] > argeodbk:base, argeodbk:linkingAttributes + + jcr:xmltext (dbk:xmltext) = dbk:xmltext * + +[dbk:lineage] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines + +[dbk:lineannotation] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines + +[dbk:link] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:markupInlines, argeodbk:publishingInlines, argeodbk:techDocInlines, argeodbk:ubiquitousInlines + - dbk:endterm (Reference) + - dbk:xrefstyle (String) + +[dbk:listitem] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements + + dbk:anchor (dbk:anchor) = dbk:anchor * + + dbk:annotation (dbk:annotation) = dbk:annotation * + + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead * + + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject * + + dbk:remark (dbk:remark) = dbk:remark * + + dbk:revhistory (dbk:revhistory) = dbk:revhistory * + + dbk:screenshot (dbk:screenshot) = dbk:screenshot * + - dbk:override (String) + +[dbk:literal] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines + +[dbk:literallayout] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:markupInlines, argeodbk:publishingInlines, argeodbk:techDocInlines, argeodbk:ubiquitousInlines + + dbk:co (dbk:co) = dbk:co * + + dbk:info (dbk:info) = dbk:info * + + dbk:lineannotation (dbk:lineannotation) = dbk:lineannotation * + + dbk:textobject (dbk:textobject) = dbk:textobject * + - dbk:class (String) + - dbk:continuation (String) + - dbk:language (String) + - dbk:linenumbering (String) + - dbk:startinglinenumber (String) + - xml:space (String) + +[dbk:locator] > argeodbk:base + - xlink:label (String) + +[dbk:manvolnum] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines + +[dbk:markup] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines + +[dbk:mathphrase] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines + + dbk:emphasis (dbk:emphasis) = dbk:emphasis * + +[dbk:mediaobject] > argeodbk:base, argeodbk:linkingAttributes + + dbk:alt (dbk:alt) = dbk:alt + + dbk:audioobject (dbk:audioobject) = dbk:audioobject * + + dbk:caption (dbk:caption) = dbk:caption + + dbk:imageobject (dbk:imageobject) = dbk:imageobject * + + dbk:imageobjectco (dbk:imageobjectco) = dbk:imageobjectco * + + dbk:info (dbk:info) = dbk:info + + dbk:textobject (dbk:textobject) = dbk:textobject * + + dbk:videoobject (dbk:videoobject) = dbk:videoobject * + +[dbk:member] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:markupInlines, argeodbk:publishingInlines, argeodbk:techDocInlines, argeodbk:ubiquitousInlines + +[dbk:menuchoice] > argeodbk:base, argeodbk:linkingAttributes + + dbk:guibutton (dbk:guibutton) = dbk:guibutton * + + dbk:guiicon (dbk:guiicon) = dbk:guiicon * + + dbk:guilabel (dbk:guilabel) = dbk:guilabel * + + dbk:guimenu (dbk:guimenu) = dbk:guimenu * + + dbk:guimenuitem (dbk:guimenuitem) = dbk:guimenuitem * + + dbk:guisubmenu (dbk:guisubmenu) = dbk:guisubmenu * + + dbk:shortcut (dbk:shortcut) = dbk:shortcut + +[dbk:methodname] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines + +[dbk:methodparam] > argeodbk:base, argeodbk:linkingAttributes + + dbk:funcparams (dbk:funcparams) = dbk:funcparams + + dbk:initializer (dbk:initializer) = dbk:initializer + + dbk:modifier (dbk:modifier) = dbk:modifier * + + dbk:modifier (dbk:modifier) = dbk:modifier * + + dbk:parameter (dbk:parameter) = dbk:parameter + + dbk:type (dbk:type) = dbk:type * + - dbk:choice (String) + - dbk:rep (String) + +[dbk:methodsynopsis] > argeodbk:base, argeodbk:linkingAttributes + + dbk:exceptionname (dbk:exceptionname) = dbk:exceptionname * + + dbk:methodname (dbk:methodname) = dbk:methodname + + dbk:methodparam (dbk:methodparam) = dbk:methodparam * + + dbk:modifier (dbk:modifier) = dbk:modifier * + + dbk:type (dbk:type) = dbk:type + + dbk:void (dbk:void) = dbk:void + - dbk:language (String) + +[dbk:modifier] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines + - xml:space (String) + +[dbk:mousebutton] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines + +[dbk:msg] > argeodbk:base, argeodbk:linkingAttributes, argeodbk:titled + + dbk:msgmain (dbk:msgmain) = dbk:msgmain + + dbk:msgrel (dbk:msgrel) = dbk:msgrel * + + dbk:msgsub (dbk:msgsub) = dbk:msgsub * + +[dbk:msgaud] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines + +[dbk:msgentry] > argeodbk:base, argeodbk:linkingAttributes + + dbk:msg (dbk:msg) = dbk:msg * + + dbk:msgexplan (dbk:msgexplan) = dbk:msgexplan * + + dbk:msginfo (dbk:msginfo) = dbk:msginfo + +[dbk:msgexplan] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled + + dbk:anchor (dbk:anchor) = dbk:anchor * + + dbk:annotation (dbk:annotation) = dbk:annotation * + + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead * + + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject * + + dbk:remark (dbk:remark) = dbk:remark * + + dbk:revhistory (dbk:revhistory) = dbk:revhistory * + + dbk:screenshot (dbk:screenshot) = dbk:screenshot * + +[dbk:msginfo] > argeodbk:base, argeodbk:linkingAttributes + + dbk:msgaud (dbk:msgaud) = dbk:msgaud * + + dbk:msglevel (dbk:msglevel) = dbk:msglevel * + + dbk:msgorig (dbk:msgorig) = dbk:msgorig * + +[dbk:msglevel] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines + +[dbk:msgmain] > argeodbk:base, argeodbk:linkingAttributes, argeodbk:titled + + dbk:msgtext (dbk:msgtext) = dbk:msgtext + +[dbk:msgorig] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines + +[dbk:msgrel] > argeodbk:base, argeodbk:linkingAttributes, argeodbk:titled + + dbk:msgtext (dbk:msgtext) = dbk:msgtext + +[dbk:msgset] > argeodbk:base, argeodbk:linkingAttributes, argeodbk:titled + + dbk:msgentry (dbk:msgentry) = dbk:msgentry * + + dbk:simplemsgentry (dbk:simplemsgentry) = dbk:simplemsgentry * + +[dbk:msgsub] > argeodbk:base, argeodbk:linkingAttributes, argeodbk:titled + + dbk:msgtext (dbk:msgtext) = dbk:msgtext + +[dbk:msgtext] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements + + dbk:anchor (dbk:anchor) = dbk:anchor * + + dbk:annotation (dbk:annotation) = dbk:annotation * + + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead * + + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject * + + dbk:remark (dbk:remark) = dbk:remark * + + dbk:revhistory (dbk:revhistory) = dbk:revhistory * + + dbk:screenshot (dbk:screenshot) = dbk:screenshot * + +[dbk:nonterminal] > argeodbk:base, argeodbk:linkingAttributes + + jcr:xmltext (dbk:xmltext) = dbk:xmltext * + - dbk:def (String) + +[dbk:note] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled + + dbk:anchor (dbk:anchor) = dbk:anchor * + + dbk:annotation (dbk:annotation) = dbk:annotation * + + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead * + + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject * + + dbk:remark (dbk:remark) = dbk:remark * + + dbk:revhistory (dbk:revhistory) = dbk:revhistory * + + dbk:screenshot (dbk:screenshot) = dbk:screenshot * + +[dbk:olink] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:markupInlines, argeodbk:publishingInlines, argeodbk:techDocInlines, argeodbk:ubiquitousInlines + - dbk:localinfo (String) + - dbk:targetdoc (String) + - dbk:targetptr (String) + - dbk:type (String) + - dbk:xrefstyle (String) + +[dbk:ooclass] > argeodbk:base, argeodbk:linkingAttributes + + dbk:classname (dbk:classname) = dbk:classname + + dbk:modifier (dbk:modifier) = dbk:modifier * + + dbk:package (dbk:package) = dbk:package * + +[dbk:ooexception] > argeodbk:base, argeodbk:linkingAttributes + + dbk:exceptionname (dbk:exceptionname) = dbk:exceptionname + + dbk:modifier (dbk:modifier) = dbk:modifier * + + dbk:package (dbk:package) = dbk:package * + +[dbk:oointerface] > argeodbk:base, argeodbk:linkingAttributes + + dbk:interfacename (dbk:interfacename) = dbk:interfacename + + dbk:modifier (dbk:modifier) = dbk:modifier * + + dbk:package (dbk:package) = dbk:package * + +[dbk:option] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines + +[dbk:optional] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines + +[dbk:orderedlist] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled + + dbk:anchor (dbk:anchor) = dbk:anchor * + + dbk:annotation (dbk:annotation) = dbk:annotation * + + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead * + + dbk:listitem (dbk:listitem) = dbk:listitem * + + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject * + + dbk:remark (dbk:remark) = dbk:remark * + + dbk:revhistory (dbk:revhistory) = dbk:revhistory * + + dbk:screenshot (dbk:screenshot) = dbk:screenshot * + - dbk:continuation (String) + - dbk:inheritnum (String) + - dbk:numeration (String) + - dbk:spacing (String) + - dbk:startingnumber (String) + +[dbk:org] > argeodbk:base, argeodbk:linkingAttributes + + dbk:address (dbk:address) = dbk:address * + + dbk:affiliation (dbk:affiliation) = dbk:affiliation * + + dbk:email (dbk:email) = dbk:email * + + dbk:orgdiv (dbk:orgdiv) = dbk:orgdiv * + + dbk:orgname (dbk:orgname) = dbk:orgname + + dbk:uri (dbk:uri) = dbk:uri * + +[dbk:orgdiv] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:markupInlines, argeodbk:publishingInlines, argeodbk:techDocInlines, argeodbk:ubiquitousInlines + +[dbk:orgname] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines + - dbk:class (String) + - dbk:otherclass (String) + +[dbk:otheraddr] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines + +[dbk:othercredit] > argeodbk:base, argeodbk:linkingAttributes + + dbk:address (dbk:address) = dbk:address * + + dbk:affiliation (dbk:affiliation) = dbk:affiliation * + + dbk:contrib (dbk:contrib) = dbk:contrib * + + dbk:email (dbk:email) = dbk:email * + + dbk:orgdiv (dbk:orgdiv) = dbk:orgdiv * + + dbk:orgname (dbk:orgname) = dbk:orgname + + dbk:personblurb (dbk:personblurb) = dbk:personblurb * + + dbk:personname (dbk:personname) = dbk:personname + + dbk:uri (dbk:uri) = dbk:uri * + - dbk:class (String) + - dbk:otherclass (String) + +[dbk:othername] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines + +[dbk:package] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines + +[dbk:pagenums] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines + +[dbk:para] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:markupInlines, argeodbk:publishingElements, argeodbk:publishingInlines, argeodbk:techDocElements, argeodbk:techDocInlines, argeodbk:ubiquitousInlines + + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead * + + dbk:info (dbk:info) = dbk:info * + + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject * + + dbk:revhistory (dbk:revhistory) = dbk:revhistory * + + dbk:screenshot (dbk:screenshot) = dbk:screenshot * + +[dbk:paramdef] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines + + dbk:funcparams (dbk:funcparams) = dbk:funcparams * + + dbk:initializer (dbk:initializer) = dbk:initializer * + + dbk:parameter (dbk:parameter) = dbk:parameter * + + dbk:type (dbk:type) = dbk:type * + - dbk:choice (String) + +[dbk:parameter] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines + - dbk:class (String) + +[dbk:part] > argeodbk:base, argeodbk:linkingAttributes, argeodbk:titled + + dbk:acknowledgements (dbk:acknowledgements) = dbk:acknowledgements * + + dbk:appendix (dbk:appendix) = dbk:appendix * + + dbk:article (dbk:article) = dbk:article * + + dbk:bibliography (dbk:bibliography) = dbk:bibliography * + + dbk:chapter (dbk:chapter) = dbk:chapter * + + dbk:colophon (dbk:colophon) = dbk:colophon * + + dbk:dedication (dbk:dedication) = dbk:dedication * + + dbk:glossary (dbk:glossary) = dbk:glossary * + + dbk:index (dbk:index) = dbk:index * + + dbk:partintro (dbk:partintro) = dbk:partintro + + dbk:preface (dbk:preface) = dbk:preface * + + dbk:refentry (dbk:refentry) = dbk:refentry * + + dbk:reference (dbk:reference) = dbk:reference * + + dbk:subtitle (dbk:subtitle) = dbk:subtitle * + + dbk:toc (dbk:toc) = dbk:toc * + - dbk:label (String) + - dbk:status (String) + +[dbk:partintro] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled + + dbk:anchor (dbk:anchor) = dbk:anchor * + + dbk:annotation (dbk:annotation) = dbk:annotation * + + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead * + + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject * + + dbk:refentry (dbk:refentry) = dbk:refentry * + + dbk:remark (dbk:remark) = dbk:remark * + + dbk:revhistory (dbk:revhistory) = dbk:revhistory * + + dbk:screenshot (dbk:screenshot) = dbk:screenshot * + + dbk:sect1 (dbk:sect1) = dbk:sect1 * + + dbk:section (dbk:section) = dbk:section * + + dbk:simplesect (dbk:simplesect) = dbk:simplesect * + + dbk:simplesect (dbk:simplesect) = dbk:simplesect * + + dbk:subtitle (dbk:subtitle) = dbk:subtitle * + - dbk:label (String) + - dbk:status (String) + +[dbk:person] > argeodbk:base, argeodbk:linkingAttributes + + dbk:address (dbk:address) = dbk:address * + + dbk:affiliation (dbk:affiliation) = dbk:affiliation * + + dbk:email (dbk:email) = dbk:email * + + dbk:personblurb (dbk:personblurb) = dbk:personblurb * + + dbk:personname (dbk:personname) = dbk:personname + + dbk:uri (dbk:uri) = dbk:uri * + +[dbk:personblurb] > argeodbk:base, argeodbk:linkingAttributes, argeodbk:paragraphElements, argeodbk:titled + + dbk:anchor (dbk:anchor) = dbk:anchor * + +[dbk:personname] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines + + dbk:firstname (dbk:firstname) = dbk:firstname * + + dbk:honorific (dbk:honorific) = dbk:honorific * + + dbk:lineage (dbk:lineage) = dbk:lineage * + + dbk:othername (dbk:othername) = dbk:othername * + + dbk:surname (dbk:surname) = dbk:surname * + +[dbk:phone] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines + +[dbk:phrase] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:markupInlines, argeodbk:publishingInlines, argeodbk:techDocInlines, argeodbk:ubiquitousInlines + +[dbk:pob] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines + +[dbk:postcode] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines + +[dbk:preface] > argeodbk:abstractSection, argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled + + dbk:anchor (dbk:anchor) = dbk:anchor * + + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject * + + dbk:refentry (dbk:refentry) = dbk:refentry * + + dbk:screenshot (dbk:screenshot) = dbk:screenshot * + + dbk:sect1 (dbk:sect1) = dbk:sect1 * + + dbk:section (dbk:section) = dbk:section * + + dbk:simplesect (dbk:simplesect) = dbk:simplesect * + + dbk:simplesect (dbk:simplesect) = dbk:simplesect * + +[dbk:primary] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:markupInlines, argeodbk:publishingInlines, argeodbk:techDocInlines, argeodbk:ubiquitousInlines + - dbk:sortas (String) + +[dbk:primaryie] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:markupInlines, argeodbk:publishingInlines, argeodbk:techDocInlines, argeodbk:ubiquitousInlines + - dbk:linkends (String) + +[dbk:printhistory] > argeodbk:base, argeodbk:linkingAttributes, argeodbk:paragraphElements + + dbk:anchor (dbk:anchor) = dbk:anchor * + +[dbk:procedure] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled + + dbk:anchor (dbk:anchor) = dbk:anchor * + + dbk:annotation (dbk:annotation) = dbk:annotation * + + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead * + + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject * + + dbk:remark (dbk:remark) = dbk:remark * + + dbk:revhistory (dbk:revhistory) = dbk:revhistory * + + dbk:screenshot (dbk:screenshot) = dbk:screenshot * + + dbk:step (dbk:step) = dbk:step * + +[dbk:production] > argeodbk:base, argeodbk:linkingAttributes + + dbk:constraint (dbk:constraint) = dbk:constraint * + + dbk:lhs (dbk:lhs) = dbk:lhs + + dbk:rhs (dbk:rhs) = dbk:rhs + +[dbk:productionrecap] > argeodbk:base, argeodbk:linkingAttributes + +[dbk:productionset] > argeodbk:base, argeodbk:linkingAttributes, argeodbk:titled + + dbk:production (dbk:production) = dbk:production * + + dbk:productionrecap (dbk:productionrecap) = dbk:productionrecap * + +[dbk:productname] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines + - dbk:class (String) + +[dbk:productnumber] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines + +[dbk:programlisting] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:markupInlines, argeodbk:publishingInlines, argeodbk:techDocInlines, argeodbk:ubiquitousInlines + + dbk:co (dbk:co) = dbk:co * + + dbk:info (dbk:info) = dbk:info * + + dbk:lineannotation (dbk:lineannotation) = dbk:lineannotation * + + dbk:textobject (dbk:textobject) = dbk:textobject * + - dbk:continuation (String) + - dbk:language (String) + - dbk:linenumbering (String) + - dbk:startinglinenumber (String) + - dbk:width (String) + - xml:space (String) + +[dbk:programlistingco] > argeodbk:base, argeodbk:linkingAttributes + + dbk:areaspec (dbk:areaspec) = dbk:areaspec + + dbk:calloutlist (dbk:calloutlist) = dbk:calloutlist * + + dbk:info (dbk:info) = dbk:info + + dbk:programlisting (dbk:programlisting) = dbk:programlisting + +[dbk:prompt] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines + + dbk:co (dbk:co) = dbk:co * + +[dbk:property] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines + +[dbk:pubdate] > argeodbk:base, argeodbk:linkingAttributes + + jcr:xmltext (dbk:xmltext) = dbk:xmltext * + +[dbk:publisher] > argeodbk:base, argeodbk:linkingAttributes + + dbk:address (dbk:address) = dbk:address * + + dbk:publishername (dbk:publishername) = dbk:publishername + +[dbk:publishername] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines + +[dbk:qandadiv] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled + + dbk:anchor (dbk:anchor) = dbk:anchor * + + dbk:annotation (dbk:annotation) = dbk:annotation * + + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead * + + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject * + + dbk:qandadiv (dbk:qandadiv) = dbk:qandadiv * + + dbk:qandaentry (dbk:qandaentry) = dbk:qandaentry * + + dbk:remark (dbk:remark) = dbk:remark * + + dbk:revhistory (dbk:revhistory) = dbk:revhistory * + + dbk:screenshot (dbk:screenshot) = dbk:screenshot * + +[dbk:qandaentry] > argeodbk:base, argeodbk:linkingAttributes, argeodbk:titled + + dbk:answer (dbk:answer) = dbk:answer * + + dbk:question (dbk:question) = dbk:question + +[dbk:qandaset] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled + + dbk:anchor (dbk:anchor) = dbk:anchor * + + dbk:annotation (dbk:annotation) = dbk:annotation * + + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead * + + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject * + + dbk:qandadiv (dbk:qandadiv) = dbk:qandadiv * + + dbk:qandaentry (dbk:qandaentry) = dbk:qandaentry * + + dbk:remark (dbk:remark) = dbk:remark * + + dbk:revhistory (dbk:revhistory) = dbk:revhistory * + + dbk:screenshot (dbk:screenshot) = dbk:screenshot * + - dbk:defaultlabel (String) + +[dbk:question] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements + + dbk:anchor (dbk:anchor) = dbk:anchor * + + dbk:annotation (dbk:annotation) = dbk:annotation * + + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead * + + dbk:label (dbk:label) = dbk:label + + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject * + + dbk:remark (dbk:remark) = dbk:remark * + + dbk:revhistory (dbk:revhistory) = dbk:revhistory * + + dbk:screenshot (dbk:screenshot) = dbk:screenshot * + +[dbk:quote] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:markupInlines, argeodbk:publishingInlines, argeodbk:techDocInlines, argeodbk:ubiquitousInlines + +[dbk:refclass] > argeodbk:base, argeodbk:linkingAttributes + + dbk:application (dbk:application) = dbk:application * + + jcr:xmltext (dbk:xmltext) = dbk:xmltext * + +[dbk:refdescriptor] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:markupInlines, argeodbk:publishingInlines, argeodbk:techDocInlines, argeodbk:ubiquitousInlines + +[dbk:refentry] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes + + dbk:info (dbk:info) = dbk:info + + dbk:refmeta (dbk:refmeta) = dbk:refmeta + + dbk:refnamediv (dbk:refnamediv) = dbk:refnamediv * + + dbk:refsect1 (dbk:refsect1) = dbk:refsect1 * + + dbk:refsection (dbk:refsection) = dbk:refsection * + + dbk:refsynopsisdiv (dbk:refsynopsisdiv) = dbk:refsynopsisdiv + - dbk:label (String) + - dbk:status (String) + +[dbk:refentrytitle] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:markupInlines, argeodbk:publishingInlines, argeodbk:techDocInlines, argeodbk:ubiquitousInlines + +[dbk:reference] > argeodbk:base, argeodbk:linkingAttributes, argeodbk:titled + + dbk:partintro (dbk:partintro) = dbk:partintro + + dbk:refentry (dbk:refentry) = dbk:refentry * + + dbk:subtitle (dbk:subtitle) = dbk:subtitle * + - dbk:label (String) + - dbk:status (String) + +[dbk:refmeta] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes + + dbk:manvolnum (dbk:manvolnum) = dbk:manvolnum + + dbk:refentrytitle (dbk:refentrytitle) = dbk:refentrytitle + + dbk:refmiscinfo (dbk:refmiscinfo) = dbk:refmiscinfo * + +[dbk:refmiscinfo] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines + - dbk:class (String) + - dbk:otherclass (String) + +[dbk:refname] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:markupInlines, argeodbk:publishingInlines, argeodbk:techDocInlines, argeodbk:ubiquitousInlines + +[dbk:refnamediv] > argeodbk:base, argeodbk:linkingAttributes + + dbk:refclass (dbk:refclass) = dbk:refclass * + + dbk:refdescriptor (dbk:refdescriptor) = dbk:refdescriptor + + dbk:refname (dbk:refname) = dbk:refname * + + dbk:refpurpose (dbk:refpurpose) = dbk:refpurpose + +[dbk:refpurpose] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:markupInlines, argeodbk:publishingInlines, argeodbk:techDocInlines, argeodbk:ubiquitousInlines + +[dbk:refsect1] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled + + dbk:anchor (dbk:anchor) = dbk:anchor * + + dbk:annotation (dbk:annotation) = dbk:annotation * + + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead * + + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject * + + dbk:refsect2 (dbk:refsect2) = dbk:refsect2 * + + dbk:refsect2 (dbk:refsect2) = dbk:refsect2 * + + dbk:remark (dbk:remark) = dbk:remark * + + dbk:revhistory (dbk:revhistory) = dbk:revhistory * + + dbk:screenshot (dbk:screenshot) = dbk:screenshot * + + dbk:subtitle (dbk:subtitle) = dbk:subtitle * + - dbk:label (String) + - dbk:status (String) + +[dbk:refsect2] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled + + dbk:anchor (dbk:anchor) = dbk:anchor * + + dbk:annotation (dbk:annotation) = dbk:annotation * + + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead * + + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject * + + dbk:refsect3 (dbk:refsect3) = dbk:refsect3 * + + dbk:refsect3 (dbk:refsect3) = dbk:refsect3 * + + dbk:remark (dbk:remark) = dbk:remark * + + dbk:revhistory (dbk:revhistory) = dbk:revhistory * + + dbk:screenshot (dbk:screenshot) = dbk:screenshot * + + dbk:subtitle (dbk:subtitle) = dbk:subtitle * + - dbk:label (String) + - dbk:status (String) + +[dbk:refsect3] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled + + dbk:anchor (dbk:anchor) = dbk:anchor * + + dbk:annotation (dbk:annotation) = dbk:annotation * + + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead * + + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject * + + dbk:remark (dbk:remark) = dbk:remark * + + dbk:revhistory (dbk:revhistory) = dbk:revhistory * + + dbk:screenshot (dbk:screenshot) = dbk:screenshot * + + dbk:subtitle (dbk:subtitle) = dbk:subtitle * + - dbk:label (String) + - dbk:status (String) + +[dbk:refsection] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled + + dbk:anchor (dbk:anchor) = dbk:anchor * + + dbk:annotation (dbk:annotation) = dbk:annotation * + + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead * + + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject * + + dbk:refsection (dbk:refsection) = dbk:refsection * + + dbk:refsection (dbk:refsection) = dbk:refsection * + + dbk:remark (dbk:remark) = dbk:remark * + + dbk:revhistory (dbk:revhistory) = dbk:revhistory * + + dbk:screenshot (dbk:screenshot) = dbk:screenshot * + + dbk:subtitle (dbk:subtitle) = dbk:subtitle * + - dbk:label (String) + - dbk:status (String) + +[dbk:refsynopsisdiv] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled + + dbk:anchor (dbk:anchor) = dbk:anchor * + + dbk:annotation (dbk:annotation) = dbk:annotation * + + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead * + + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject * + + dbk:refsect2 (dbk:refsect2) = dbk:refsect2 * + + dbk:refsection (dbk:refsection) = dbk:refsection * + + dbk:remark (dbk:remark) = dbk:remark * + + dbk:revhistory (dbk:revhistory) = dbk:revhistory * + + dbk:screenshot (dbk:screenshot) = dbk:screenshot * + + dbk:subtitle (dbk:subtitle) = dbk:subtitle * + +[dbk:releaseinfo] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines + +[dbk:remark] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines + +[dbk:replaceable] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines + + dbk:co (dbk:co) = dbk:co * + - dbk:class (String) + +[dbk:returnvalue] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines + +[dbk:revdescription] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements + + dbk:anchor (dbk:anchor) = dbk:anchor * + + dbk:annotation (dbk:annotation) = dbk:annotation * + + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead * + + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject * + + dbk:remark (dbk:remark) = dbk:remark * + + dbk:revhistory (dbk:revhistory) = dbk:revhistory * + + dbk:screenshot (dbk:screenshot) = dbk:screenshot * + +[dbk:revhistory] > argeodbk:base, argeodbk:linkingAttributes, argeodbk:titled + + dbk:revision (dbk:revision) = dbk:revision * + +[dbk:revision] > argeodbk:base, argeodbk:linkingAttributes + + dbk:author (dbk:author) = dbk:author * + + dbk:authorinitials (dbk:authorinitials) = dbk:authorinitials * + + dbk:date (dbk:date) = dbk:date + + dbk:revdescription (dbk:revdescription) = dbk:revdescription + + dbk:revnumber (dbk:revnumber) = dbk:revnumber + + dbk:revremark (dbk:revremark) = dbk:revremark + +[dbk:revnumber] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines + +[dbk:revremark] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines + +[dbk:rhs] > argeodbk:base, argeodbk:linkingAttributes + + dbk:lineannotation (dbk:lineannotation) = dbk:lineannotation * + + dbk:nonterminal (dbk:nonterminal) = dbk:nonterminal * + + dbk:sbr (dbk:sbr) = dbk:sbr * + + jcr:xmltext (dbk:xmltext) = dbk:xmltext * + +[dbk:row] > argeodbk:base, argeodbk:linkingAttributes + + dbk:entry (dbk:entry) = dbk:entry * + + dbk:entrytbl (dbk:entrytbl) = dbk:entrytbl * + - dbk:rowsep (String) + - dbk:valign (String) + +[dbk:sbr] > argeodbk:base + +[dbk:screen] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:markupInlines, argeodbk:publishingInlines, argeodbk:techDocInlines, argeodbk:ubiquitousInlines + + dbk:co (dbk:co) = dbk:co * + + dbk:info (dbk:info) = dbk:info * + + dbk:lineannotation (dbk:lineannotation) = dbk:lineannotation * + + dbk:textobject (dbk:textobject) = dbk:textobject * + - dbk:continuation (String) + - dbk:language (String) + - dbk:linenumbering (String) + - dbk:startinglinenumber (String) + - dbk:width (String) + - xml:space (String) + +[dbk:screenco] > argeodbk:base, argeodbk:linkingAttributes + + dbk:areaspec (dbk:areaspec) = dbk:areaspec + + dbk:calloutlist (dbk:calloutlist) = dbk:calloutlist * + + dbk:info (dbk:info) = dbk:info + + dbk:screen (dbk:screen) = dbk:screen + +[dbk:screenshot] > argeodbk:base, argeodbk:linkingAttributes, argeodbk:titled + + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject + + dbk:subtitle (dbk:subtitle) = dbk:subtitle * + +[dbk:secondary] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:markupInlines, argeodbk:publishingInlines, argeodbk:techDocInlines, argeodbk:ubiquitousInlines + - dbk:sortas (String) + +[dbk:secondaryie] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:markupInlines, argeodbk:publishingInlines, argeodbk:techDocInlines, argeodbk:ubiquitousInlines + - dbk:linkends (String) + +[dbk:sect1] > argeodbk:abstractSection, argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled + + dbk:anchor (dbk:anchor) = dbk:anchor * + + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject * + + dbk:screenshot (dbk:screenshot) = dbk:screenshot * + + dbk:sect2 (dbk:sect2) = dbk:sect2 * + + dbk:simplesect (dbk:simplesect) = dbk:simplesect * + + dbk:simplesect (dbk:simplesect) = dbk:simplesect * + +[dbk:sect2] > argeodbk:abstractSection, argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled + + dbk:anchor (dbk:anchor) = dbk:anchor * + + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject * + + dbk:screenshot (dbk:screenshot) = dbk:screenshot * + + dbk:sect3 (dbk:sect3) = dbk:sect3 * + + dbk:simplesect (dbk:simplesect) = dbk:simplesect * + + dbk:simplesect (dbk:simplesect) = dbk:simplesect * + +[dbk:sect3] > argeodbk:abstractSection, argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled + + dbk:anchor (dbk:anchor) = dbk:anchor * + + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject * + + dbk:screenshot (dbk:screenshot) = dbk:screenshot * + + dbk:sect4 (dbk:sect4) = dbk:sect4 * + + dbk:simplesect (dbk:simplesect) = dbk:simplesect * + + dbk:simplesect (dbk:simplesect) = dbk:simplesect * + +[dbk:sect4] > argeodbk:abstractSection, argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled + + dbk:anchor (dbk:anchor) = dbk:anchor * + + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject * + + dbk:screenshot (dbk:screenshot) = dbk:screenshot * + + dbk:sect5 (dbk:sect5) = dbk:sect5 * + + dbk:simplesect (dbk:simplesect) = dbk:simplesect * + + dbk:simplesect (dbk:simplesect) = dbk:simplesect * + +[dbk:sect5] > argeodbk:abstractSection, argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled + + dbk:anchor (dbk:anchor) = dbk:anchor * + + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject * + + dbk:screenshot (dbk:screenshot) = dbk:screenshot * + + dbk:simplesect (dbk:simplesect) = dbk:simplesect * + + dbk:simplesect (dbk:simplesect) = dbk:simplesect * + +[dbk:section] > argeodbk:abstractSection, argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled + + dbk:anchor (dbk:anchor) = dbk:anchor * + + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject * + + dbk:refentry (dbk:refentry) = dbk:refentry * + + dbk:screenshot (dbk:screenshot) = dbk:screenshot * + + dbk:section (dbk:section) = dbk:section * + + dbk:simplesect (dbk:simplesect) = dbk:simplesect * + + dbk:simplesect (dbk:simplesect) = dbk:simplesect * + +[dbk:see] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:markupInlines, argeodbk:publishingInlines, argeodbk:techDocInlines, argeodbk:ubiquitousInlines + +[dbk:seealso] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:markupInlines, argeodbk:publishingInlines, argeodbk:techDocInlines, argeodbk:ubiquitousInlines + +[dbk:seealsoie] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:markupInlines, argeodbk:publishingInlines, argeodbk:techDocInlines, argeodbk:ubiquitousInlines + - dbk:linkends (String) + +[dbk:seeie] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:markupInlines, argeodbk:publishingInlines, argeodbk:techDocInlines, argeodbk:ubiquitousInlines + +[dbk:seg] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:markupInlines, argeodbk:publishingInlines, argeodbk:techDocInlines, argeodbk:ubiquitousInlines + +[dbk:seglistitem] > argeodbk:base, argeodbk:linkingAttributes + + dbk:seg (dbk:seg) = dbk:seg * + +[dbk:segmentedlist] > argeodbk:base, argeodbk:linkingAttributes, argeodbk:titled + + dbk:seglistitem (dbk:seglistitem) = dbk:seglistitem * + + dbk:segtitle (dbk:segtitle) = dbk:segtitle * + +[dbk:segtitle] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:markupInlines, argeodbk:publishingInlines, argeodbk:techDocInlines, argeodbk:ubiquitousInlines + +[dbk:seriesvolnums] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines + +[dbk:set] > argeodbk:base, argeodbk:linkingAttributes, argeodbk:titled + + dbk:book (dbk:book) = dbk:book * + + dbk:set (dbk:set) = dbk:set * + + dbk:setindex (dbk:setindex) = dbk:setindex + + dbk:subtitle (dbk:subtitle) = dbk:subtitle * + + dbk:toc (dbk:toc) = dbk:toc + - dbk:label (String) + - dbk:status (String) + +[dbk:setindex] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled + + dbk:anchor (dbk:anchor) = dbk:anchor * + + dbk:annotation (dbk:annotation) = dbk:annotation * + + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead * + + dbk:indexdiv (dbk:indexdiv) = dbk:indexdiv * + + dbk:indexentry (dbk:indexentry) = dbk:indexentry * + + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject * + + dbk:remark (dbk:remark) = dbk:remark * + + dbk:revhistory (dbk:revhistory) = dbk:revhistory * + + dbk:screenshot (dbk:screenshot) = dbk:screenshot * + + dbk:subtitle (dbk:subtitle) = dbk:subtitle * + - dbk:label (String) + - dbk:status (String) + - dbk:type (String) + +[dbk:shortaffil] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines + +[dbk:shortcut] > argeodbk:base, argeodbk:linkingAttributes + + dbk:keycap (dbk:keycap) = dbk:keycap * + + dbk:keycombo (dbk:keycombo) = dbk:keycombo * + + dbk:keysym (dbk:keysym) = dbk:keysym * + + dbk:mousebutton (dbk:mousebutton) = dbk:mousebutton * + - dbk:action (String) + - dbk:otheraction (String) + +[dbk:sidebar] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled + + dbk:anchor (dbk:anchor) = dbk:anchor * + + dbk:annotation (dbk:annotation) = dbk:annotation * + + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead * + + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject * + + dbk:remark (dbk:remark) = dbk:remark * + + dbk:revhistory (dbk:revhistory) = dbk:revhistory * + + dbk:screenshot (dbk:screenshot) = dbk:screenshot * + +[dbk:simpara] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:markupInlines, argeodbk:publishingInlines, argeodbk:techDocInlines, argeodbk:ubiquitousInlines + + dbk:info (dbk:info) = dbk:info * + +[dbk:simplelist] > argeodbk:base, argeodbk:linkingAttributes + + dbk:member (dbk:member) = dbk:member * + - dbk:columns (String) + - dbk:type (String) + +[dbk:simplemsgentry] > argeodbk:base, argeodbk:linkingAttributes + + dbk:msgexplan (dbk:msgexplan) = dbk:msgexplan * + + dbk:msgtext (dbk:msgtext) = dbk:msgtext + - dbk:msgaud (String) + - dbk:msglevel (String) + - dbk:msgorig (String) + +[dbk:simplesect] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled + + dbk:anchor (dbk:anchor) = dbk:anchor * + + dbk:annotation (dbk:annotation) = dbk:annotation * + + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead * + + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject * + + dbk:remark (dbk:remark) = dbk:remark * + + dbk:revhistory (dbk:revhistory) = dbk:revhistory * + + dbk:screenshot (dbk:screenshot) = dbk:screenshot * + + dbk:subtitle (dbk:subtitle) = dbk:subtitle * + - dbk:label (String) + - dbk:status (String) + +[dbk:spanspec] > argeodbk:base, argeodbk:linkingAttributes + - dbk:align (String) + - dbk:char (String) + - dbk:charoff (String) + - dbk:colsep (String) + - dbk:nameend (String) + - dbk:namest (String) + - dbk:rowsep (String) + - dbk:spanname (String) + +[dbk:state] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines + +[dbk:step] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled + + dbk:anchor (dbk:anchor) = dbk:anchor * + + dbk:annotation (dbk:annotation) = dbk:annotation * + + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead * + + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject * + + dbk:remark (dbk:remark) = dbk:remark * + + dbk:revhistory (dbk:revhistory) = dbk:revhistory * + + dbk:screenshot (dbk:screenshot) = dbk:screenshot * + + dbk:stepalternatives (dbk:stepalternatives) = dbk:stepalternatives + + dbk:substeps (dbk:substeps) = dbk:substeps + - dbk:performance (String) + +[dbk:stepalternatives] > argeodbk:base, argeodbk:linkingAttributes + + dbk:info (dbk:info) = dbk:info + + dbk:step (dbk:step) = dbk:step * + - dbk:performance (String) + +[dbk:street] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines + +[dbk:subject] > argeodbk:base, argeodbk:linkingAttributes + + dbk:subjectterm (dbk:subjectterm) = dbk:subjectterm * + - dbk:weight (String) + +[dbk:subjectset] > argeodbk:base, argeodbk:linkingAttributes + + dbk:subject (dbk:subject) = dbk:subject * + - dbk:scheme (String) + +[dbk:subjectterm] > argeodbk:base, argeodbk:linkingAttributes + + jcr:xmltext (dbk:xmltext) = dbk:xmltext * + +[dbk:subscript] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines + +[dbk:substeps] > argeodbk:base, argeodbk:linkingAttributes + + dbk:step (dbk:step) = dbk:step * + - dbk:performance (String) + +[dbk:subtitle] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:markupInlines, argeodbk:publishingInlines, argeodbk:techDocInlines, argeodbk:ubiquitousInlines + +[dbk:superscript] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines + +[dbk:surname] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines + +[dbk:symbol] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines + - dbk:class (String) + +[dbk:synopfragment] > argeodbk:base, argeodbk:linkingAttributes + + dbk:arg (dbk:arg) = dbk:arg * + + dbk:group (dbk:group) = dbk:group * + +[dbk:synopfragmentref] > argeodbk:base, argeodbk:linkingAttributes + + jcr:xmltext (dbk:xmltext) = dbk:xmltext * + +[dbk:synopsis] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:markupInlines, argeodbk:publishingInlines, argeodbk:techDocInlines, argeodbk:ubiquitousInlines + + dbk:co (dbk:co) = dbk:co * + + dbk:info (dbk:info) = dbk:info * + + dbk:lineannotation (dbk:lineannotation) = dbk:lineannotation * + + dbk:textobject (dbk:textobject) = dbk:textobject * + - dbk:continuation (String) + - dbk:label (String) + - dbk:language (String) + - dbk:linenumbering (String) + - dbk:startinglinenumber (String) + - xml:space (String) + +[dbk:systemitem] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines + + dbk:co (dbk:co) = dbk:co * + - dbk:class (String) + +[dbk:table] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:titled + + dbk:caption (dbk:caption) = dbk:caption + + dbk:col (dbk:col) = dbk:col * + + dbk:colgroup (dbk:colgroup) = dbk:colgroup * + + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject * + + dbk:tbody (dbk:tbody) = dbk:tbody * + + dbk:textobject (dbk:textobject) = dbk:textobject * + + dbk:tfoot (dbk:tfoot) = dbk:tfoot + + dbk:tgroup (dbk:tgroup) = dbk:tgroup * + + dbk:thead (dbk:thead) = dbk:thead + + dbk:tr (dbk:tr) = dbk:tr * + - dbk:border (String) + - dbk:cellpadding (String) + - dbk:cellspacing (String) + - dbk:class (String) + - dbk:colsep (String) + - dbk:floatstyle (String) + - dbk:frame (String) + - dbk:label (String) + - dbk:lang (String) + - dbk:onclick (String) + - dbk:ondblclick (String) + - dbk:onkeydown (String) + - dbk:onkeypress (String) + - dbk:onkeyup (String) + - dbk:onmousedown (String) + - dbk:onmousemove (String) + - dbk:onmouseout (String) + - dbk:onmouseover (String) + - dbk:onmouseup (String) + - dbk:orient (String) + - dbk:pgwide (String) + - dbk:rowheader (String) + - dbk:rowsep (String) + - dbk:rules (String) + - dbk:shortentry (String) + - dbk:style (String) + - dbk:summary (String) + - dbk:tabstyle (String) + - dbk:title (String) + - dbk:tocentry (String) + - dbk:width (String) + +[dbk:tag] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines + - dbk:class (String) + - dbk:namespace (String) + +[dbk:task] > argeodbk:base, argeodbk:linkingAttributes, argeodbk:titled + + dbk:example (dbk:example) = dbk:example * + + dbk:procedure (dbk:procedure) = dbk:procedure + + dbk:subtitle (dbk:subtitle) = dbk:subtitle * + + dbk:taskprerequisites (dbk:taskprerequisites) = dbk:taskprerequisites + + dbk:taskrelated (dbk:taskrelated) = dbk:taskrelated + + dbk:tasksummary (dbk:tasksummary) = dbk:tasksummary + +[dbk:taskprerequisites] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled + + dbk:anchor (dbk:anchor) = dbk:anchor * + + dbk:annotation (dbk:annotation) = dbk:annotation * + + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead * + + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject * + + dbk:remark (dbk:remark) = dbk:remark * + + dbk:revhistory (dbk:revhistory) = dbk:revhistory * + + dbk:screenshot (dbk:screenshot) = dbk:screenshot * + +[dbk:taskrelated] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled + + dbk:anchor (dbk:anchor) = dbk:anchor * + + dbk:annotation (dbk:annotation) = dbk:annotation * + + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead * + + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject * + + dbk:remark (dbk:remark) = dbk:remark * + + dbk:revhistory (dbk:revhistory) = dbk:revhistory * + + dbk:screenshot (dbk:screenshot) = dbk:screenshot * + +[dbk:tasksummary] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled + + dbk:anchor (dbk:anchor) = dbk:anchor * + + dbk:annotation (dbk:annotation) = dbk:annotation * + + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead * + + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject * + + dbk:remark (dbk:remark) = dbk:remark * + + dbk:revhistory (dbk:revhistory) = dbk:revhistory * + + dbk:screenshot (dbk:screenshot) = dbk:screenshot * + +[dbk:tbody] > argeodbk:base, argeodbk:linkingAttributes + + dbk:row (dbk:row) = dbk:row * + + dbk:tr (dbk:tr) = dbk:tr * + - dbk:align (String) + - dbk:char (String) + - dbk:charoff (String) + - dbk:class (String) + - dbk:lang (String) + - dbk:onclick (String) + - dbk:ondblclick (String) + - dbk:onkeydown (String) + - dbk:onkeypress (String) + - dbk:onkeyup (String) + - dbk:onmousedown (String) + - dbk:onmousemove (String) + - dbk:onmouseout (String) + - dbk:onmouseover (String) + - dbk:onmouseup (String) + - dbk:style (String) + - dbk:title (String) + - dbk:valign (String) + +[dbk:td] > argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:listElements, argeodbk:markupInlines, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:publishingInlines, argeodbk:techDocElements, argeodbk:techDocInlines, argeodbk:ubiquitousInlines + + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead * + + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject * + + dbk:revhistory (dbk:revhistory) = dbk:revhistory * + + dbk:screenshot (dbk:screenshot) = dbk:screenshot * + - dbk:abbr (String) + - dbk:align (String) + - dbk:annotations (String) + - dbk:arch (String) + - dbk:audience (String) + - dbk:axis (String) + - dbk:char (String) + - dbk:charoff (String) + - dbk:class (String) + - dbk:colspan (String) + - dbk:condition (String) + - dbk:conformance (String) + - dbk:dir (String) + - dbk:headers (String) + - dbk:lang (String) + - dbk:onclick (String) + - dbk:ondblclick (String) + - dbk:onkeydown (String) + - dbk:onkeypress (String) + - dbk:onkeyup (String) + - dbk:onmousedown (String) + - dbk:onmousemove (String) + - dbk:onmouseout (String) + - dbk:onmouseover (String) + - dbk:onmouseup (String) + - dbk:os (String) + - dbk:remap (String) + - dbk:revision (String) + - dbk:revisionflag (String) + - dbk:rowspan (String) + - dbk:scope (String) + - dbk:security (String) + - dbk:style (String) + - dbk:title (String) + - dbk:userlevel (String) + - dbk:valign (String) + - dbk:vendor (String) + - dbk:version (String) + - dbk:wordsize (String) + - dbk:xreflabel (String) + - xml:base (String) + - xml:id (String) + - xml:lang (String) + +[dbk:term] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:markupInlines, argeodbk:publishingInlines, argeodbk:techDocInlines, argeodbk:ubiquitousInlines + +[dbk:termdef] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:markupInlines, argeodbk:publishingInlines, argeodbk:techDocInlines, argeodbk:ubiquitousInlines + - dbk:baseform (String) + - dbk:sortas (String) + +[dbk:tertiary] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:markupInlines, argeodbk:publishingInlines, argeodbk:techDocInlines, argeodbk:ubiquitousInlines + - dbk:sortas (String) + +[dbk:tertiaryie] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:markupInlines, argeodbk:publishingInlines, argeodbk:techDocInlines, argeodbk:ubiquitousInlines + - dbk:linkends (String) + +[dbk:textdata] > argeodbk:base + + dbk:info (dbk:info) = dbk:info + - dbk:encoding (String) + - dbk:entityref (String) + - dbk:fileref (String) + - dbk:format (String) + +[dbk:textobject] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements + + dbk:anchor (dbk:anchor) = dbk:anchor * + + dbk:annotation (dbk:annotation) = dbk:annotation * + + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead * + + dbk:info (dbk:info) = dbk:info + + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject * + + dbk:phrase (dbk:phrase) = dbk:phrase + + dbk:remark (dbk:remark) = dbk:remark * + + dbk:revhistory (dbk:revhistory) = dbk:revhistory * + + dbk:screenshot (dbk:screenshot) = dbk:screenshot * + + dbk:textdata (dbk:textdata) = dbk:textdata + +[dbk:tfoot] > argeodbk:base, argeodbk:linkingAttributes + + dbk:colspec (dbk:colspec) = dbk:colspec * + + dbk:row (dbk:row) = dbk:row * + + dbk:tr (dbk:tr) = dbk:tr * + - dbk:align (String) + - dbk:char (String) + - dbk:charoff (String) + - dbk:class (String) + - dbk:lang (String) + - dbk:onclick (String) + - dbk:ondblclick (String) + - dbk:onkeydown (String) + - dbk:onkeypress (String) + - dbk:onkeyup (String) + - dbk:onmousedown (String) + - dbk:onmousemove (String) + - dbk:onmouseout (String) + - dbk:onmouseover (String) + - dbk:onmouseup (String) + - dbk:style (String) + - dbk:title (String) + - dbk:valign (String) + +[dbk:tgroup] > argeodbk:base, argeodbk:linkingAttributes + + dbk:colspec (dbk:colspec) = dbk:colspec * + + dbk:spanspec (dbk:spanspec) = dbk:spanspec * + + dbk:tbody (dbk:tbody) = dbk:tbody + + dbk:tfoot (dbk:tfoot) = dbk:tfoot + + dbk:thead (dbk:thead) = dbk:thead + - dbk:align (String) + - dbk:char (String) + - dbk:charoff (String) + - dbk:cols (String) + - dbk:colsep (String) + - dbk:rowsep (String) + - dbk:tgroupstyle (String) + +[dbk:th] > argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:listElements, argeodbk:markupInlines, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:publishingInlines, argeodbk:techDocElements, argeodbk:techDocInlines, argeodbk:ubiquitousInlines + + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead * + + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject * + + dbk:revhistory (dbk:revhistory) = dbk:revhistory * + + dbk:screenshot (dbk:screenshot) = dbk:screenshot * + - dbk:abbr (String) + - dbk:align (String) + - dbk:annotations (String) + - dbk:arch (String) + - dbk:audience (String) + - dbk:axis (String) + - dbk:char (String) + - dbk:charoff (String) + - dbk:class (String) + - dbk:colspan (String) + - dbk:condition (String) + - dbk:conformance (String) + - dbk:dir (String) + - dbk:headers (String) + - dbk:lang (String) + - dbk:onclick (String) + - dbk:ondblclick (String) + - dbk:onkeydown (String) + - dbk:onkeypress (String) + - dbk:onkeyup (String) + - dbk:onmousedown (String) + - dbk:onmousemove (String) + - dbk:onmouseout (String) + - dbk:onmouseover (String) + - dbk:onmouseup (String) + - dbk:os (String) + - dbk:remap (String) + - dbk:revision (String) + - dbk:revisionflag (String) + - dbk:rowspan (String) + - dbk:scope (String) + - dbk:security (String) + - dbk:style (String) + - dbk:title (String) + - dbk:userlevel (String) + - dbk:valign (String) + - dbk:vendor (String) + - dbk:version (String) + - dbk:wordsize (String) + - dbk:xreflabel (String) + - xml:base (String) + - xml:id (String) + - xml:lang (String) + +[dbk:thead] > argeodbk:base, argeodbk:linkingAttributes + + dbk:colspec (dbk:colspec) = dbk:colspec * + + dbk:row (dbk:row) = dbk:row * + + dbk:tr (dbk:tr) = dbk:tr * + - dbk:align (String) + - dbk:char (String) + - dbk:charoff (String) + - dbk:class (String) + - dbk:lang (String) + - dbk:onclick (String) + - dbk:ondblclick (String) + - dbk:onkeydown (String) + - dbk:onkeypress (String) + - dbk:onkeyup (String) + - dbk:onmousedown (String) + - dbk:onmousemove (String) + - dbk:onmouseout (String) + - dbk:onmouseover (String) + - dbk:onmouseup (String) + - dbk:style (String) + - dbk:title (String) + - dbk:valign (String) + +[dbk:tip] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled + + dbk:anchor (dbk:anchor) = dbk:anchor * + + dbk:annotation (dbk:annotation) = dbk:annotation * + + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead * + + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject * + + dbk:remark (dbk:remark) = dbk:remark * + + dbk:revhistory (dbk:revhistory) = dbk:revhistory * + + dbk:screenshot (dbk:screenshot) = dbk:screenshot * + +[dbk:title] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:markupInlines, argeodbk:publishingInlines, argeodbk:techDocInlines, argeodbk:ubiquitousInlines + +[dbk:titleabbrev] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:markupInlines, argeodbk:publishingInlines, argeodbk:techDocInlines, argeodbk:ubiquitousInlines + +[dbk:toc] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled + + dbk:anchor (dbk:anchor) = dbk:anchor * + + dbk:annotation (dbk:annotation) = dbk:annotation * + + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead * + + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject * + + dbk:remark (dbk:remark) = dbk:remark * + + dbk:revhistory (dbk:revhistory) = dbk:revhistory * + + dbk:screenshot (dbk:screenshot) = dbk:screenshot * + + dbk:tocdiv (dbk:tocdiv) = dbk:tocdiv * + + dbk:tocentry (dbk:tocentry) = dbk:tocentry * + +[dbk:tocdiv] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled + + dbk:anchor (dbk:anchor) = dbk:anchor * + + dbk:annotation (dbk:annotation) = dbk:annotation * + + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead * + + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject * + + dbk:remark (dbk:remark) = dbk:remark * + + dbk:revhistory (dbk:revhistory) = dbk:revhistory * + + dbk:screenshot (dbk:screenshot) = dbk:screenshot * + + dbk:subtitle (dbk:subtitle) = dbk:subtitle * + + dbk:tocdiv (dbk:tocdiv) = dbk:tocdiv * + + dbk:tocentry (dbk:tocentry) = dbk:tocentry * + - dbk:pagenum (String) + +[dbk:tocentry] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:markupInlines, argeodbk:publishingInlines, argeodbk:techDocInlines, argeodbk:ubiquitousInlines + - dbk:pagenum (String) + +[dbk:token] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines + +[dbk:tr] > nt:base + + dbk:td (dbk:td) = dbk:td * + + dbk:th (dbk:th) = dbk:th * + - dbk:align (String) + - dbk:annotations (String) + - dbk:arch (String) + - dbk:audience (String) + - dbk:char (String) + - dbk:charoff (String) + - dbk:class (String) + - dbk:condition (String) + - dbk:conformance (String) + - dbk:dir (String) + - dbk:lang (String) + - dbk:onclick (String) + - dbk:ondblclick (String) + - dbk:onkeydown (String) + - dbk:onkeypress (String) + - dbk:onkeyup (String) + - dbk:onmousedown (String) + - dbk:onmousemove (String) + - dbk:onmouseout (String) + - dbk:onmouseover (String) + - dbk:onmouseup (String) + - dbk:os (String) + - dbk:remap (String) + - dbk:revision (String) + - dbk:revisionflag (String) + - dbk:security (String) + - dbk:style (String) + - dbk:title (String) + - dbk:userlevel (String) + - dbk:valign (String) + - dbk:vendor (String) + - dbk:version (String) + - dbk:wordsize (String) + - dbk:xreflabel (String) + - xml:base (String) + - xml:id (String) + - xml:lang (String) + +[dbk:trademark] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines + - dbk:class (String) + +[dbk:type] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines + +[dbk:uri] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines + - dbk:type (String) + +[dbk:userinput] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:markupInlines, argeodbk:ubiquitousInlines + + dbk:accel (dbk:accel) = dbk:accel * + + dbk:co (dbk:co) = dbk:co * + + dbk:command (dbk:command) = dbk:command * + + dbk:computeroutput (dbk:computeroutput) = dbk:computeroutput * + + dbk:envar (dbk:envar) = dbk:envar * + + dbk:filename (dbk:filename) = dbk:filename * + + dbk:guibutton (dbk:guibutton) = dbk:guibutton * + + dbk:guiicon (dbk:guiicon) = dbk:guiicon * + + dbk:guilabel (dbk:guilabel) = dbk:guilabel * + + dbk:guimenu (dbk:guimenu) = dbk:guimenu * + + dbk:guimenuitem (dbk:guimenuitem) = dbk:guimenuitem * + + dbk:guisubmenu (dbk:guisubmenu) = dbk:guisubmenu * + + dbk:keycap (dbk:keycap) = dbk:keycap * + + dbk:keycode (dbk:keycode) = dbk:keycode * + + dbk:keycombo (dbk:keycombo) = dbk:keycombo * + + dbk:keysym (dbk:keysym) = dbk:keysym * + + dbk:menuchoice (dbk:menuchoice) = dbk:menuchoice * + + dbk:mousebutton (dbk:mousebutton) = dbk:mousebutton * + + dbk:nonterminal (dbk:nonterminal) = dbk:nonterminal * + + dbk:option (dbk:option) = dbk:option * + + dbk:optional (dbk:optional) = dbk:optional * + + dbk:package (dbk:package) = dbk:package * + + dbk:parameter (dbk:parameter) = dbk:parameter * + + dbk:prompt (dbk:prompt) = dbk:prompt * + + dbk:property (dbk:property) = dbk:property * + + dbk:replaceable (dbk:replaceable) = dbk:replaceable * + + dbk:shortcut (dbk:shortcut) = dbk:shortcut * + + dbk:systemitem (dbk:systemitem) = dbk:systemitem * + + dbk:termdef (dbk:termdef) = dbk:termdef * + + dbk:userinput (dbk:userinput) = dbk:userinput * + + jcr:xmltext (dbk:xmltext) = dbk:xmltext * + +[dbk:varargs] > argeodbk:base, argeodbk:linkingAttributes + +[dbk:variablelist] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled + + dbk:anchor (dbk:anchor) = dbk:anchor * + + dbk:annotation (dbk:annotation) = dbk:annotation * + + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead * + + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject * + + dbk:remark (dbk:remark) = dbk:remark * + + dbk:revhistory (dbk:revhistory) = dbk:revhistory * + + dbk:screenshot (dbk:screenshot) = dbk:screenshot * + + dbk:varlistentry (dbk:varlistentry) = dbk:varlistentry * + - dbk:spacing (String) + - dbk:termlength (String) + +[dbk:varlistentry] > argeodbk:base, argeodbk:linkingAttributes + + dbk:listitem (dbk:listitem) = dbk:listitem + + dbk:term (dbk:term) = dbk:term * + +[dbk:varname] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines + +[dbk:videodata] > argeodbk:base + + dbk:info (dbk:info) = dbk:info + - dbk:align (String) + - dbk:contentdepth (String) + - dbk:contentwidth (String) + - dbk:depth (String) + - dbk:entityref (String) + - dbk:fileref (String) + - dbk:format (String) + - dbk:scale (String) + - dbk:scalefit (String) + - dbk:valign (String) + - dbk:width (String) + +[dbk:videoobject] > argeodbk:base, argeodbk:linkingAttributes + + dbk:info (dbk:info) = dbk:info + + dbk:videodata (dbk:videodata) = dbk:videodata + +[dbk:void] > argeodbk:base, argeodbk:linkingAttributes + +[dbk:volumenum] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines + +[dbk:warning] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled + + dbk:anchor (dbk:anchor) = dbk:anchor * + + dbk:annotation (dbk:annotation) = dbk:annotation * + + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead * + + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject * + + dbk:remark (dbk:remark) = dbk:remark * + + dbk:revhistory (dbk:revhistory) = dbk:revhistory * + + dbk:screenshot (dbk:screenshot) = dbk:screenshot * + +[dbk:wordasword] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines + +[dbk:xmltext] > nt:base + - jcr:xmlcharacters (String) + +[dbk:xref] > argeodbk:base, argeodbk:linkingAttributes + - dbk:endterm (Reference) + - dbk:xrefstyle (String) + +[dbk:year] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines + +[xs:anyType] > nt:base + + * (nt:base) + + jcr:xmltext (dbk:xmltext) = dbk:xmltext * + - * (undefined) + + diff --git a/org.argeo.jcr/src/org/argeo/jcr/docbook/docbook.cnd b/org.argeo.jcr/src/org/argeo/jcr/docbook/docbook.cnd new file mode 100644 index 000000000..c737cee63 --- /dev/null +++ b/org.argeo.jcr/src/org/argeo/jcr/docbook/docbook.cnd @@ -0,0 +1,531 @@ + + + + +[argeodbk:titled] +mixin + + dbk:info (dbk:info) = dbk:info * + + dbk:title (dbk:title) = dbk:title * + +[argeodbk:linkingAttributes] +mixin + - dbk:linkend (String) + - xlink:actuate (String) + - xlink:arcrole (String) + - xlink:href (String) + - xlink:role (String) + - xlink:show (String) + - xlink:title (String) + - xlink:type (String) + +[argeodbk:freeText] +mixin + + dbk:phrase (dbk:phrase) = dbk:phrase * + + dbk:replaceable (dbk:replaceable) = dbk:replaceable * + + jcr:xmltext (dbk:xmltext) = dbk:xmltext * + +[argeodbk:markupInlines] +mixin + +[argeodbk:listElements] +mixin + + dbk:itemizedlist (dbk:itemizedlist) = dbk:itemizedlist * + + dbk:orderedlist (dbk:orderedlist) = dbk:orderedlist * + + dbk:simplelist (dbk:simplelist) = dbk:simplelist * + +[argeodbk:paragraphElements] +mixin + + dbk:para (dbk:para) = dbk:para * + +[argeodbk:indexingInlines] +mixin + +[argeodbk:techDocElements] +mixin + + dbk:table (dbk:table) = dbk:table * + +[argeodbk:techDocInlines] +mixin + +[argeodbk:publishingElements] +mixin + +[argeodbk:ubiquitousInlines] +mixin + + dbk:alt (dbk:alt) = dbk:alt * + + dbk:anchor (dbk:anchor) = dbk:anchor * + + dbk:annotation (dbk:annotation) = dbk:annotation * + + dbk:biblioref (dbk:biblioref) = dbk:biblioref * + + dbk:inlinemediaobject (dbk:inlinemediaobject) = dbk:inlinemediaobject * + + dbk:link (dbk:link) = dbk:link * + + dbk:olink (dbk:olink) = dbk:olink * + + dbk:remark (dbk:remark) = dbk:remark * + + dbk:subscript (dbk:subscript) = dbk:subscript * + + dbk:superscript (dbk:superscript) = dbk:superscript * + + dbk:xref (dbk:xref) = dbk:xref * + +[argeodbk:abstractSection] +mixin + + dbk:annotation (dbk:annotation) = dbk:annotation * + + dbk:remark (dbk:remark) = dbk:remark * + + dbk:subtitle (dbk:subtitle) = dbk:subtitle * + - dbk:label (String) + - dbk:status (String) + +[argeodbk:bibliographyInlines] +mixin + + dbk:author (dbk:author) = dbk:author * + + dbk:editor (dbk:editor) = dbk:editor * + + dbk:orgname (dbk:orgname) = dbk:orgname * + + dbk:personname (dbk:personname) = dbk:personname * + +[argeodbk:publishingInlines] +mixin + + dbk:emphasis (dbk:emphasis) = dbk:emphasis * + +[argeodbk:base] +abstract + - dbk:annotations (String) + - dbk:arch (String) + - dbk:audience (String) + - dbk:condition (String) + - dbk:conformance (String) + - dbk:dir (String) + - dbk:os (String) + - dbk:remap (String) + - dbk:revision (String) + - dbk:revisionflag (String) + - dbk:role (String) + - dbk:security (String) + - dbk:userlevel (String) + - dbk:vendor (String) + - dbk:version (String) + - dbk:wordsize (String) + - dbk:xreflabel (String) +// - {http://www.w3.org/XML/1998/namespace}base (String) +// - {http://www.w3.org/XML/1998/namespace}id (String) +// - {http://www.w3.org/XML/1998/namespace}lang (String) + +[dbk:alt] > argeodbk:base + + dbk:inlinemediaobject (dbk:inlinemediaobject) = dbk:inlinemediaobject * + + jcr:xmltext (dbk:xmltext) = dbk:xmltext * + +[dbk:anchor] > argeodbk:base + +[dbk:annotation] > argeodbk:base, argeodbk:indexingInlines, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled + + dbk:anchor (dbk:anchor) = dbk:anchor * + + dbk:annotation (dbk:annotation) = dbk:annotation * + + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject * + + dbk:remark (dbk:remark) = dbk:remark * + - dbk:annotates (String) + +[dbk:article] > argeodbk:abstractSection, argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled + + dbk:anchor (dbk:anchor) = dbk:anchor * + + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject * + + dbk:section (dbk:section) = dbk:section * + - dbk:class (String) + +[dbk:audiodata] > argeodbk:base + + dbk:info (dbk:info) = dbk:info + - dbk:entityref (String) + - dbk:fileref (String) + - dbk:format (String) + +[dbk:audioobject] > argeodbk:base, argeodbk:linkingAttributes + + dbk:audiodata (dbk:audiodata) = dbk:audiodata + + dbk:info (dbk:info) = dbk:info + +[dbk:author] > argeodbk:base, argeodbk:linkingAttributes + + dbk:orgdiv (dbk:orgdiv) = dbk:orgdiv * + + dbk:orgname (dbk:orgname) = dbk:orgname + + dbk:personblurb (dbk:personblurb) = dbk:personblurb * + + dbk:personname (dbk:personname) = dbk:personname + +[dbk:biblioref] > argeodbk:base, argeodbk:linkingAttributes + - dbk:begin (String) + - dbk:end (String) + - dbk:endterm (Reference) + - dbk:units (String) + - dbk:xrefstyle (String) + +[dbk:book] > argeodbk:base, argeodbk:linkingAttributes, argeodbk:titled + + dbk:article (dbk:article) = dbk:article * + + dbk:chapter (dbk:chapter) = dbk:chapter * + + dbk:subtitle (dbk:subtitle) = dbk:subtitle * + - dbk:label (String) + - dbk:status (String) + +[dbk:caption] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements + + dbk:anchor (dbk:anchor) = dbk:anchor * + + dbk:annotation (dbk:annotation) = dbk:annotation * + + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject * + + dbk:remark (dbk:remark) = dbk:remark * + + jcr:xmltext (dbk:xmltext) = dbk:xmltext * + - dbk:class (String) + - dbk:lang (String) + - dbk:onclick (String) + - dbk:ondblclick (String) + - dbk:onkeydown (String) + - dbk:onkeypress (String) + - dbk:onkeyup (String) + - dbk:onmousedown (String) + - dbk:onmousemove (String) + - dbk:onmouseout (String) + - dbk:onmouseover (String) + - dbk:onmouseup (String) + - dbk:style (String) + - dbk:title (String) + +[dbk:chapter] > argeodbk:abstractSection, argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled + + dbk:anchor (dbk:anchor) = dbk:anchor * + + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject * + + dbk:section (dbk:section) = dbk:section * + +[dbk:colspec] > argeodbk:base, argeodbk:linkingAttributes + - dbk:align (String) + - dbk:char (String) + - dbk:charoff (String) + - dbk:colname (String) + - dbk:colnum (String) + - dbk:colsep (String) + - dbk:colwidth (String) + - dbk:rowsep (String) + +[dbk:editor] > argeodbk:base, argeodbk:linkingAttributes + + dbk:orgdiv (dbk:orgdiv) = dbk:orgdiv * + + dbk:orgname (dbk:orgname) = dbk:orgname + + dbk:personblurb (dbk:personblurb) = dbk:personblurb * + + dbk:personname (dbk:personname) = dbk:personname + +[dbk:emphasis] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:markupInlines, argeodbk:publishingInlines, argeodbk:techDocInlines, argeodbk:ubiquitousInlines + +[dbk:entry] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:markupInlines, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:publishingInlines, argeodbk:techDocElements, argeodbk:techDocInlines, argeodbk:ubiquitousInlines + + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject * + - dbk:align (String) + - dbk:char (String) + - dbk:charoff (String) + - dbk:colname (String) + - dbk:colsep (String) + - dbk:morerows (String) + - dbk:nameend (String) + - dbk:namest (String) + - dbk:rotate (String) + - dbk:rowsep (String) + - dbk:spanname (String) + - dbk:valign (String) + +[dbk:entrytbl] > argeodbk:base, argeodbk:linkingAttributes + + dbk:colspec (dbk:colspec) = dbk:colspec * + + dbk:spanspec (dbk:spanspec) = dbk:spanspec * + + dbk:tbody (dbk:tbody) = dbk:tbody + + dbk:thead (dbk:thead) = dbk:thead + - dbk:align (String) + - dbk:char (String) + - dbk:charoff (String) + - dbk:colname (String) + - dbk:cols (String) + - dbk:colsep (String) + - dbk:nameend (String) + - dbk:namest (String) + - dbk:rowsep (String) + - dbk:spanname (String) + - dbk:tgroupstyle (String) + +[dbk:imagedata] > argeodbk:base + + dbk:info (dbk:info) = dbk:info + - dbk:align (String) + - dbk:contentdepth (String) + - dbk:contentwidth (String) + - dbk:depth (String) + - dbk:entityref (String) + - dbk:fileref (String) + - dbk:format (String) + - dbk:scale (String) + - dbk:scalefit (String) + - dbk:valign (String) + - dbk:width (String) + +[dbk:imageobject] > argeodbk:base, argeodbk:linkingAttributes + + dbk:imagedata (dbk:imagedata) = dbk:imagedata + + dbk:info (dbk:info) = dbk:info + +[dbk:info] > argeodbk:base + + dbk:annotation (dbk:annotation) = dbk:annotation * + + dbk:author (dbk:author) = dbk:author * + + dbk:editor (dbk:editor) = dbk:editor * + + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject * + + dbk:orgname (dbk:orgname) = dbk:orgname * + + dbk:subtitle (dbk:subtitle) = dbk:subtitle * + + dbk:title (dbk:title) = dbk:title * + +[dbk:inlinemediaobject] > argeodbk:base, argeodbk:linkingAttributes + + dbk:alt (dbk:alt) = dbk:alt + + dbk:audioobject (dbk:audioobject) = dbk:audioobject * + + dbk:imageobject (dbk:imageobject) = dbk:imageobject * + + dbk:info (dbk:info) = dbk:info + + dbk:textobject (dbk:textobject) = dbk:textobject * + + dbk:videoobject (dbk:videoobject) = dbk:videoobject * + +[dbk:itemizedlist] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled + + dbk:anchor (dbk:anchor) = dbk:anchor * + + dbk:annotation (dbk:annotation) = dbk:annotation * + + dbk:listitem (dbk:listitem) = dbk:listitem * + + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject * + + dbk:remark (dbk:remark) = dbk:remark * + - dbk:mark (String) + - dbk:spacing (String) + +[dbk:link] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:markupInlines, argeodbk:publishingInlines, argeodbk:techDocInlines, argeodbk:ubiquitousInlines + - dbk:endterm (Reference) + - dbk:xrefstyle (String) + +[dbk:listitem] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements + + dbk:anchor (dbk:anchor) = dbk:anchor * + + dbk:annotation (dbk:annotation) = dbk:annotation * + + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject * + + dbk:remark (dbk:remark) = dbk:remark * + - dbk:override (String) + +[dbk:mediaobject] > argeodbk:base, argeodbk:linkingAttributes + + dbk:alt (dbk:alt) = dbk:alt + + dbk:audioobject (dbk:audioobject) = dbk:audioobject * + + dbk:caption (dbk:caption) = dbk:caption + + dbk:imageobject (dbk:imageobject) = dbk:imageobject * + + dbk:info (dbk:info) = dbk:info + + dbk:textobject (dbk:textobject) = dbk:textobject * + + dbk:videoobject (dbk:videoobject) = dbk:videoobject * + +[dbk:olink] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:markupInlines, argeodbk:publishingInlines, argeodbk:techDocInlines, argeodbk:ubiquitousInlines + - dbk:localinfo (String) + - dbk:targetdoc (String) + - dbk:targetptr (String) + - dbk:type (String) + - dbk:xrefstyle (String) + +[dbk:orderedlist] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled + + dbk:anchor (dbk:anchor) = dbk:anchor * + + dbk:annotation (dbk:annotation) = dbk:annotation * + + dbk:listitem (dbk:listitem) = dbk:listitem * + + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject * + + dbk:remark (dbk:remark) = dbk:remark * + - dbk:continuation (String) + - dbk:inheritnum (String) + - dbk:numeration (String) + - dbk:spacing (String) + - dbk:startingnumber (String) + +[dbk:orgdiv] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:markupInlines, argeodbk:publishingInlines, argeodbk:techDocInlines, argeodbk:ubiquitousInlines + +[dbk:orgname] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines + - dbk:class (String) + - dbk:otherclass (String) + +[dbk:para] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:markupInlines, argeodbk:publishingElements, argeodbk:publishingInlines, argeodbk:techDocElements, argeodbk:techDocInlines, argeodbk:ubiquitousInlines + + dbk:info (dbk:info) = dbk:info * + + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject * + +[dbk:personblurb] > argeodbk:base, argeodbk:linkingAttributes, argeodbk:paragraphElements, argeodbk:titled + + dbk:anchor (dbk:anchor) = dbk:anchor * + +[dbk:personname] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines + +[dbk:phrase] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:markupInlines, argeodbk:publishingInlines, argeodbk:techDocInlines, argeodbk:ubiquitousInlines + +[dbk:remark] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines + +[dbk:replaceable] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines + - dbk:class (String) + +[dbk:row] > argeodbk:base, argeodbk:linkingAttributes + + dbk:entry (dbk:entry) = dbk:entry * + + dbk:entrytbl (dbk:entrytbl) = dbk:entrytbl * + - dbk:rowsep (String) + - dbk:valign (String) + +[dbk:section] > argeodbk:abstractSection, argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled + + dbk:anchor (dbk:anchor) = dbk:anchor * + + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject * + + dbk:section (dbk:section) = dbk:section * + +[dbk:set] > argeodbk:base, argeodbk:linkingAttributes, argeodbk:titled + + dbk:book (dbk:book) = dbk:book * + + dbk:set (dbk:set) = dbk:set * + + dbk:subtitle (dbk:subtitle) = dbk:subtitle * + - dbk:label (String) + - dbk:status (String) + +[dbk:simplelist] > argeodbk:base, argeodbk:linkingAttributes + - dbk:columns (String) + - dbk:type (String) + +[dbk:spanspec] > argeodbk:base, argeodbk:linkingAttributes + - dbk:align (String) + - dbk:char (String) + - dbk:charoff (String) + - dbk:colsep (String) + - dbk:nameend (String) + - dbk:namest (String) + - dbk:rowsep (String) + - dbk:spanname (String) + +[dbk:subscript] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines + +[dbk:subtitle] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:markupInlines, argeodbk:publishingInlines, argeodbk:techDocInlines, argeodbk:ubiquitousInlines + +[dbk:superscript] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines + +[dbk:table] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:titled + + dbk:caption (dbk:caption) = dbk:caption + + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject * + + dbk:tbody (dbk:tbody) = dbk:tbody * + + dbk:textobject (dbk:textobject) = dbk:textobject * + + dbk:tfoot (dbk:tfoot) = dbk:tfoot + + dbk:tgroup (dbk:tgroup) = dbk:tgroup * + + dbk:thead (dbk:thead) = dbk:thead + - dbk:border (String) + - dbk:cellpadding (String) + - dbk:cellspacing (String) + - dbk:class (String) + - dbk:colsep (String) + - dbk:floatstyle (String) + - dbk:frame (String) + - dbk:label (String) + - dbk:lang (String) + - dbk:onclick (String) + - dbk:ondblclick (String) + - dbk:onkeydown (String) + - dbk:onkeypress (String) + - dbk:onkeyup (String) + - dbk:onmousedown (String) + - dbk:onmousemove (String) + - dbk:onmouseout (String) + - dbk:onmouseover (String) + - dbk:onmouseup (String) + - dbk:orient (String) + - dbk:pgwide (String) + - dbk:rowheader (String) + - dbk:rowsep (String) + - dbk:rules (String) + - dbk:shortentry (String) + - dbk:style (String) + - dbk:summary (String) + - dbk:tabstyle (String) + - dbk:title (String) + - dbk:tocentry (String) + - dbk:width (String) + +[dbk:tbody] > argeodbk:base, argeodbk:linkingAttributes + + dbk:row (dbk:row) = dbk:row * + - dbk:align (String) + - dbk:char (String) + - dbk:charoff (String) + - dbk:class (String) + - dbk:lang (String) + - dbk:onclick (String) + - dbk:ondblclick (String) + - dbk:onkeydown (String) + - dbk:onkeypress (String) + - dbk:onkeyup (String) + - dbk:onmousedown (String) + - dbk:onmousemove (String) + - dbk:onmouseout (String) + - dbk:onmouseover (String) + - dbk:onmouseup (String) + - dbk:style (String) + - dbk:title (String) + - dbk:valign (String) + +[dbk:textobject] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements + + dbk:anchor (dbk:anchor) = dbk:anchor * + + dbk:annotation (dbk:annotation) = dbk:annotation * + + dbk:info (dbk:info) = dbk:info + + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject * + + dbk:phrase (dbk:phrase) = dbk:phrase + + dbk:remark (dbk:remark) = dbk:remark * + +[dbk:tfoot] > argeodbk:base, argeodbk:linkingAttributes + + dbk:colspec (dbk:colspec) = dbk:colspec * + + dbk:row (dbk:row) = dbk:row * + - dbk:align (String) + - dbk:char (String) + - dbk:charoff (String) + - dbk:class (String) + - dbk:lang (String) + - dbk:onclick (String) + - dbk:ondblclick (String) + - dbk:onkeydown (String) + - dbk:onkeypress (String) + - dbk:onkeyup (String) + - dbk:onmousedown (String) + - dbk:onmousemove (String) + - dbk:onmouseout (String) + - dbk:onmouseover (String) + - dbk:onmouseup (String) + - dbk:style (String) + - dbk:title (String) + - dbk:valign (String) + +[dbk:tgroup] > argeodbk:base, argeodbk:linkingAttributes + + dbk:colspec (dbk:colspec) = dbk:colspec * + + dbk:spanspec (dbk:spanspec) = dbk:spanspec * + + dbk:tbody (dbk:tbody) = dbk:tbody + + dbk:tfoot (dbk:tfoot) = dbk:tfoot + + dbk:thead (dbk:thead) = dbk:thead + - dbk:align (String) + - dbk:char (String) + - dbk:charoff (String) + - dbk:cols (String) + - dbk:colsep (String) + - dbk:rowsep (String) + - dbk:tgroupstyle (String) + +[dbk:thead] > argeodbk:base, argeodbk:linkingAttributes + + dbk:colspec (dbk:colspec) = dbk:colspec * + + dbk:row (dbk:row) = dbk:row * + - dbk:align (String) + - dbk:char (String) + - dbk:charoff (String) + - dbk:class (String) + - dbk:lang (String) + - dbk:onclick (String) + - dbk:ondblclick (String) + - dbk:onkeydown (String) + - dbk:onkeypress (String) + - dbk:onkeyup (String) + - dbk:onmousedown (String) + - dbk:onmousemove (String) + - dbk:onmouseout (String) + - dbk:onmouseover (String) + - dbk:onmouseup (String) + - dbk:style (String) + - dbk:title (String) + - dbk:valign (String) + +[dbk:title] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:markupInlines, argeodbk:publishingInlines, argeodbk:techDocInlines, argeodbk:ubiquitousInlines + +[dbk:videodata] > argeodbk:base + + dbk:info (dbk:info) = dbk:info + - dbk:align (String) + - dbk:contentdepth (String) + - dbk:contentwidth (String) + - dbk:depth (String) + - dbk:entityref (String) + - dbk:fileref (String) + - dbk:format (String) + - dbk:scale (String) + - dbk:scalefit (String) + - dbk:valign (String) + - dbk:width (String) + +[dbk:videoobject] > argeodbk:base, argeodbk:linkingAttributes + + dbk:info (dbk:info) = dbk:info + + dbk:videodata (dbk:videodata) = dbk:videodata + +[dbk:xmltext] > nt:base + - jcr:xmlcharacters (String) + +[dbk:xref] > argeodbk:base, argeodbk:linkingAttributes + - dbk:endterm (Reference) + - dbk:xrefstyle (String) + + diff --git a/org.argeo.jcr/src/org/argeo/jcr/fs/JcrFileStore.java b/org.argeo.jcr/src/org/argeo/jcr/fs/JcrFileStore.java new file mode 100644 index 000000000..32a3ecb5f --- /dev/null +++ b/org.argeo.jcr/src/org/argeo/jcr/fs/JcrFileStore.java @@ -0,0 +1,72 @@ +package org.argeo.jcr.fs; + +import java.io.IOException; +import java.nio.file.FileStore; +import java.nio.file.attribute.FileAttributeView; +import java.nio.file.attribute.FileStoreAttributeView; + +public class JcrFileStore extends FileStore { + + @Override + public String name() { + // TODO Auto-generated method stub + return null; + } + + @Override + public String type() { + // TODO Auto-generated method stub + return null; + } + + @Override + public boolean isReadOnly() { + // TODO Auto-generated method stub + return false; + } + + @Override + public long getTotalSpace() throws IOException { + // TODO Auto-generated method stub + return 0; + } + + @Override + public long getUsableSpace() throws IOException { + // TODO Auto-generated method stub + return 0; + } + + @Override + public long getUnallocatedSpace() throws IOException { + // TODO Auto-generated method stub + return 0; + } + + @Override + public boolean supportsFileAttributeView( + Class type) { + // TODO Auto-generated method stub + return false; + } + + @Override + public boolean supportsFileAttributeView(String name) { + // TODO Auto-generated method stub + return false; + } + + @Override + public V getFileStoreAttributeView( + Class type) { + // TODO Auto-generated method stub + return null; + } + + @Override + public Object getAttribute(String attribute) throws IOException { + // TODO Auto-generated method stub + return null; + } + +} diff --git a/org.argeo.jcr/src/org/argeo/jcr/fs/JcrFileSystem.java b/org.argeo.jcr/src/org/argeo/jcr/fs/JcrFileSystem.java new file mode 100644 index 000000000..40328e8a0 --- /dev/null +++ b/org.argeo.jcr/src/org/argeo/jcr/fs/JcrFileSystem.java @@ -0,0 +1,97 @@ +package org.argeo.jcr.fs; + +import java.io.IOException; +import java.nio.file.FileStore; +import java.nio.file.FileSystem; +import java.nio.file.Path; +import java.nio.file.PathMatcher; +import java.nio.file.WatchService; +import java.nio.file.attribute.UserPrincipalLookupService; +import java.nio.file.spi.FileSystemProvider; +import java.util.Set; + +import javax.jcr.Session; + +import org.argeo.jcr.JcrUtils; + +public class JcrFileSystem extends FileSystem { + private final JcrFileSystemProvider provider; + private final Session session; + + public JcrFileSystem(JcrFileSystemProvider provider, Session session) { + super(); + this.provider = provider; + this.session = session; + } + + @Override + public FileSystemProvider provider() { + return provider; + } + + @Override + public void close() throws IOException { + JcrUtils.logoutQuietly(session); + } + + @Override + public boolean isOpen() { + return session.isLive(); + } + + @Override + public boolean isReadOnly() { + return false; + } + + @Override + public String getSeparator() { + return "/"; + } + + @Override + public Iterable getRootDirectories() { + return null; + } + + @Override + public Iterable getFileStores() { + // TODO Auto-generated method stub + return null; + } + + @Override + public Set supportedFileAttributeViews() { + // TODO Auto-generated method stub + return null; + } + + @Override + public Path getPath(String first, String... more) { + // TODO Auto-generated method stub + return null; + } + + @Override + public PathMatcher getPathMatcher(String syntaxAndPattern) { + // TODO Auto-generated method stub + return null; + } + + @Override + public UserPrincipalLookupService getUserPrincipalLookupService() { + // TODO Auto-generated method stub + return null; + } + + @Override + public WatchService newWatchService() throws IOException { + // TODO Auto-generated method stub + return null; + } + + public Session getSession() { + return session; + } + +} diff --git a/org.argeo.jcr/src/org/argeo/jcr/fs/JcrFileSystemProvider.java b/org.argeo.jcr/src/org/argeo/jcr/fs/JcrFileSystemProvider.java new file mode 100644 index 000000000..8ea4cca2e --- /dev/null +++ b/org.argeo.jcr/src/org/argeo/jcr/fs/JcrFileSystemProvider.java @@ -0,0 +1,142 @@ +package org.argeo.jcr.fs; + +import java.io.IOException; +import java.net.URI; +import java.nio.channels.SeekableByteChannel; +import java.nio.file.AccessMode; +import java.nio.file.CopyOption; +import java.nio.file.DirectoryStream; +import java.nio.file.DirectoryStream.Filter; +import java.nio.file.FileStore; +import java.nio.file.FileSystem; +import java.nio.file.LinkOption; +import java.nio.file.OpenOption; +import java.nio.file.Path; +import java.nio.file.attribute.BasicFileAttributes; +import java.nio.file.attribute.FileAttribute; +import java.nio.file.attribute.FileAttributeView; +import java.nio.file.spi.FileSystemProvider; +import java.util.Map; +import java.util.Set; + +public class JcrFileSystemProvider extends FileSystemProvider { + + @Override + public String getScheme() { + return "jcr"; + } + + @Override + public FileSystem newFileSystem(URI uri, Map env) + throws IOException { + // TODO Auto-generated method stub + return null; + } + + @Override + public FileSystem getFileSystem(URI uri) { + // TODO Auto-generated method stub + return null; + } + + @Override + public Path getPath(URI uri) { + // TODO Auto-generated method stub + return null; + } + + @Override + public SeekableByteChannel newByteChannel(Path path, + Set options, FileAttribute... attrs) + throws IOException { + // TODO Auto-generated method stub + return null; + } + + @Override + public DirectoryStream newDirectoryStream(Path dir, + Filter filter) throws IOException { + // TODO Auto-generated method stub + return null; + } + + @Override + public void createDirectory(Path dir, FileAttribute... attrs) + throws IOException { + // TODO Auto-generated method stub + + } + + @Override + public void delete(Path path) throws IOException { + // TODO Auto-generated method stub + + } + + @Override + public void copy(Path source, Path target, CopyOption... options) + throws IOException { + // TODO Auto-generated method stub + + } + + @Override + public void move(Path source, Path target, CopyOption... options) + throws IOException { + // TODO Auto-generated method stub + + } + + @Override + public boolean isSameFile(Path path, Path path2) throws IOException { + // TODO Auto-generated method stub + return false; + } + + @Override + public boolean isHidden(Path path) throws IOException { + // TODO Auto-generated method stub + return false; + } + + @Override + public FileStore getFileStore(Path path) throws IOException { + // TODO Auto-generated method stub + return null; + } + + @Override + public void checkAccess(Path path, AccessMode... modes) throws IOException { + // TODO Auto-generated method stub + + } + + @Override + public V getFileAttributeView(Path path, + Class type, LinkOption... options) { + // TODO Auto-generated method stub + return null; + } + + @Override + public A readAttributes(Path path, + Class type, LinkOption... options) throws IOException { + // TODO Auto-generated method stub + return null; + } + + @Override + public Map readAttributes(Path path, String attributes, + LinkOption... options) throws IOException { + // TODO Auto-generated method stub + return null; + } + + @Override + public void setAttribute(Path path, String attribute, Object value, + LinkOption... options) throws IOException { + // TODO Auto-generated method stub + + } + +} diff --git a/org.argeo.jcr/src/org/argeo/jcr/fs/JcrFsException.java b/org.argeo.jcr/src/org/argeo/jcr/fs/JcrFsException.java new file mode 100644 index 000000000..f214fdc44 --- /dev/null +++ b/org.argeo.jcr/src/org/argeo/jcr/fs/JcrFsException.java @@ -0,0 +1,15 @@ +package org.argeo.jcr.fs; + + +/** Exception related to the JCR FS */ +public class JcrFsException extends RuntimeException { + private static final long serialVersionUID = -7973896038244922980L; + + public JcrFsException(String message, Throwable e) { + super(message, e); + } + + public JcrFsException(String message) { + super(message); + } +} diff --git a/org.argeo.jcr/src/org/argeo/jcr/fs/JcrPath.java b/org.argeo.jcr/src/org/argeo/jcr/fs/JcrPath.java new file mode 100644 index 000000000..e25293517 --- /dev/null +++ b/org.argeo.jcr/src/org/argeo/jcr/fs/JcrPath.java @@ -0,0 +1,199 @@ +package org.argeo.jcr.fs; + +import java.io.File; +import java.io.IOException; +import java.net.URI; +import java.nio.file.FileSystem; +import java.nio.file.LinkOption; +import java.nio.file.Path; +import java.nio.file.WatchEvent.Kind; +import java.nio.file.WatchEvent.Modifier; +import java.nio.file.WatchKey; +import java.nio.file.WatchService; +import java.util.Iterator; + +import javax.jcr.Node; +import javax.jcr.RepositoryException; + +public class JcrPath implements Path { + private JcrFileSystem filesSystem; + private String path; + + private Node node; + + public JcrPath(JcrFileSystem filesSystem, Node node) { + super(); + this.filesSystem = filesSystem; + this.node = node; + } + + @Override + public FileSystem getFileSystem() { + return filesSystem; + } + + @Override + public boolean isAbsolute() { + return path.startsWith("/"); + } + + @Override + public Path getRoot() { + try { + return new JcrPath(filesSystem, node.getSession().getRootNode()); + } catch (RepositoryException e) { + throw new JcrFsException("Cannot get root", e); + } + } + + @Override + public Path getFileName() { + return null; + } + + @Override + public Path getParent() { + // TODO Auto-generated method stub + return null; + } + + @Override + public int getNameCount() { + // TODO Auto-generated method stub + return 0; + } + + @Override + public Path getName(int index) { + // TODO Auto-generated method stub + return null; + } + + @Override + public Path subpath(int beginIndex, int endIndex) { + // TODO Auto-generated method stub + return null; + } + + @Override + public boolean startsWith(Path other) { + // TODO Auto-generated method stub + return false; + } + + @Override + public boolean startsWith(String other) { + // TODO Auto-generated method stub + return false; + } + + @Override + public boolean endsWith(Path other) { + // TODO Auto-generated method stub + return false; + } + + @Override + public boolean endsWith(String other) { + // TODO Auto-generated method stub + return false; + } + + @Override + public Path normalize() { + // TODO Auto-generated method stub + return null; + } + + @Override + public Path resolve(Path other) { + // TODO Auto-generated method stub + return null; + } + + @Override + public Path resolve(String other) { + // TODO Auto-generated method stub + return null; + } + + @Override + public Path resolveSibling(Path other) { + // TODO Auto-generated method stub + return null; + } + + @Override + public Path resolveSibling(String other) { + // TODO Auto-generated method stub + return null; + } + + @Override + public Path relativize(Path other) { + // TODO Auto-generated method stub + return null; + } + + @Override + public URI toUri() { + // TODO Auto-generated method stub + return null; + } + + @Override + public Path toAbsolutePath() { + // TODO Auto-generated method stub + return null; + } + + @Override + public Path toRealPath(LinkOption... options) throws IOException { + // TODO Auto-generated method stub + return null; + } + + @Override + public File toFile() { + throw new UnsupportedOperationException(); + } + + @Override + public WatchKey register(WatchService watcher, Kind[] events, + Modifier... modifiers) throws IOException { + // TODO Auto-generated method stub + return null; + } + + @Override + public WatchKey register(WatchService watcher, Kind... events) + throws IOException { + // TODO Auto-generated method stub + return null; + } + + @Override + public Iterator iterator() { + // TODO Auto-generated method stub + return null; + } + + @Override + public int compareTo(Path other) { + // TODO Auto-generated method stub + return 0; + } + + public Node getNode() { + if (!isAbsolute())// TODO default dir + throw new JcrFsException("Cannot get node from relative path"); + try { + if (node == null) + node = filesSystem.getSession().getNode(path); + return node; + } catch (RepositoryException e) { + throw new JcrFsException("Cannot get node", e); + } + } + +} diff --git a/org.argeo.jcr/src/org/argeo/jcr/proxy/AbstractUrlProxy.java b/org.argeo.jcr/src/org/argeo/jcr/proxy/AbstractUrlProxy.java new file mode 100644 index 000000000..30369ce7d --- /dev/null +++ b/org.argeo.jcr/src/org/argeo/jcr/proxy/AbstractUrlProxy.java @@ -0,0 +1,178 @@ +/* + * 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.jcr.proxy; + +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; + +import javax.jcr.Binary; +import javax.jcr.Node; +import javax.jcr.Property; +import javax.jcr.Repository; +import javax.jcr.RepositoryException; +import javax.jcr.Session; +import javax.jcr.nodetype.NodeType; + +import org.apache.commons.io.IOUtils; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.argeo.jcr.ArgeoJcrException; +import org.argeo.jcr.JcrUtils; + +/** Base class for URL based proxys. */ +public abstract class AbstractUrlProxy implements ResourceProxy { + private final static Log log = LogFactory.getLog(AbstractUrlProxy.class); + + private Repository jcrRepository; + private Session jcrAdminSession; + private String proxyWorkspace = "proxy"; + + protected abstract Node retrieve(Session session, String path); + + void init() { + try { + jcrAdminSession = JcrUtils.loginOrCreateWorkspace(jcrRepository, + proxyWorkspace); + beforeInitSessionSave(jcrAdminSession); + if (jcrAdminSession.hasPendingChanges()) + jcrAdminSession.save(); + } catch (Exception e) { + JcrUtils.discardQuietly(jcrAdminSession); + throw new ArgeoJcrException("Cannot initialize Maven proxy", e); + } + } + + /** + * Called before the (admin) session is saved at the end of the + * initialization. Does nothing by default, to be overridden. + */ + protected void beforeInitSessionSave(Session session) + throws RepositoryException { + } + + void destroy() { + JcrUtils.logoutQuietly(jcrAdminSession); + } + + /** + * Called before the (admin) session is logged out when resources are + * released. Does nothing by default, to be overridden. + */ + protected void beforeDestroySessionLogout() throws RepositoryException { + } + + public Node proxy(String path) { + // we open a JCR session with client credentials in order not to use the + // admin session in multiple thread or make it a bottleneck. + Node nodeAdmin = null; + Node nodeClient = null; + Session clientSession = null; + try { + clientSession = jcrRepository.login(proxyWorkspace); + if (!clientSession.itemExists(path) + || shouldUpdate(clientSession, path)) { + nodeAdmin = retrieveAndSave(path); + if (nodeAdmin != null) + nodeClient = clientSession.getNode(path); + } else + nodeClient = clientSession.getNode(path); + return nodeClient; + } catch (RepositoryException e) { + throw new ArgeoJcrException("Cannot proxy " + path, e); + } finally { + if (nodeClient == null) + JcrUtils.logoutQuietly(clientSession); + } + } + + protected synchronized Node retrieveAndSave(String path) { + try { + Node node = retrieve(jcrAdminSession, path); + if (node == null) + return null; + jcrAdminSession.save(); + return node; + } catch (RepositoryException e) { + JcrUtils.discardQuietly(jcrAdminSession); + throw new ArgeoJcrException("Cannot retrieve and save " + path, e); + } finally { + notifyAll(); + } + } + + /** Session is not saved */ + protected synchronized Node proxyUrl(Session session, String remoteUrl, + String path) throws RepositoryException { + Node node = null; + if (session.itemExists(path)) { + // throw new ArgeoJcrException("Node " + path + " already exists"); + } + InputStream in = null; + try { + URL u = new URL(remoteUrl); + in = u.openStream(); + node = importFile(session, path, in); + } catch (IOException e) { + if (log.isDebugEnabled()) { + log.debug("Cannot read " + remoteUrl + ", skipping... " + + e.getMessage()); + // log.trace("Cannot read because of ", e); + } + JcrUtils.discardQuietly(session); + } finally { + IOUtils.closeQuietly(in); + } + return node; + } + + protected synchronized Node importFile(Session session, String path, + InputStream in) throws RepositoryException { + Binary binary = null; + try { + Node content = null; + Node node = null; + if (!session.itemExists(path)) { + node = JcrUtils.mkdirs(session, path, NodeType.NT_FILE, + NodeType.NT_FOLDER, false); + content = node.addNode(Node.JCR_CONTENT, NodeType.NT_RESOURCE); + } else { + node = session.getNode(path); + content = node.getNode(Node.JCR_CONTENT); + } + binary = session.getValueFactory().createBinary(in); + content.setProperty(Property.JCR_DATA, binary); + JcrUtils.updateLastModifiedAndParents(node, null); + return node; + } finally { + JcrUtils.closeQuietly(binary); + } + } + + /** Whether the file should be updated. */ + protected Boolean shouldUpdate(Session clientSession, String nodePath) { + return false; + } + + public void setJcrRepository(Repository jcrRepository) { + this.jcrRepository = jcrRepository; + } + + public void setProxyWorkspace(String localWorkspace) { + this.proxyWorkspace = localWorkspace; + } + +} diff --git a/org.argeo.jcr/src/org/argeo/jcr/proxy/ResourceProxy.java b/org.argeo.jcr/src/org/argeo/jcr/proxy/ResourceProxy.java new file mode 100644 index 000000000..b4fb33286 --- /dev/null +++ b/org.argeo.jcr/src/org/argeo/jcr/proxy/ResourceProxy.java @@ -0,0 +1,31 @@ +/* + * 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.jcr.proxy; + +import javax.jcr.Node; + +/** A proxy which nows how to resolve and synchronize relative URLs */ +public interface ResourceProxy { + /** + * Proxy the file referenced by this relative path in the underlying + * repository. A new session is created by each call, so the underlying + * session of the returned node must be closed by the caller. + * + * @return the proxied Node, null if the resource was not found + * (e.g. HTTP 404) + */ + public Node proxy(String relativePath); +} diff --git a/org.argeo.jcr/src/org/argeo/jcr/proxy/ResourceProxyServlet.java b/org.argeo.jcr/src/org/argeo/jcr/proxy/ResourceProxyServlet.java new file mode 100644 index 000000000..e92e2a402 --- /dev/null +++ b/org.argeo.jcr/src/org/argeo/jcr/proxy/ResourceProxyServlet.java @@ -0,0 +1,140 @@ +/* + * 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.jcr.proxy; + +import java.io.IOException; +import java.io.InputStream; + +import javax.jcr.Binary; +import javax.jcr.Node; +import javax.jcr.PathNotFoundException; +import javax.jcr.Property; +import javax.jcr.RepositoryException; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +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.argeo.jcr.ArgeoJcrException; +import org.argeo.jcr.JcrUtils; + +/** Wraps a proxy via HTTP */ +public class ResourceProxyServlet extends HttpServlet { + private static final long serialVersionUID = -8886549549223155801L; + + private final static Log log = LogFactory + .getLog(ResourceProxyServlet.class); + + private ResourceProxy proxy; + + private String contentTypeCharset = "UTF-8"; + + @Override + protected void doGet(HttpServletRequest request, + HttpServletResponse response) throws ServletException, IOException { + String path = request.getPathInfo(); + + if (log.isTraceEnabled()) { + log.trace("path=" + path); + log.trace("UserPrincipal = " + request.getUserPrincipal().getName()); + log.trace("SessionID = " + request.getSession().getId()); + log.trace("ContextPath = " + request.getContextPath()); + log.trace("ServletPath = " + request.getServletPath()); + log.trace("PathInfo = " + request.getPathInfo()); + log.trace("Method = " + request.getMethod()); + log.trace("User-Agent = " + request.getHeader("User-Agent")); + } + + Node node = null; + try { + node = proxy.proxy(path); + if (node == null) + response.sendError(404); + else + processResponse(node, response); + } finally { + if (node != null) + try { + JcrUtils.logoutQuietly(node.getSession()); + } catch (RepositoryException e) { + // silent + } + } + + } + + /** Retrieve the content of the node. */ + protected void processResponse(Node node, HttpServletResponse response) { + Binary binary = null; + InputStream in = null; + try { + String fileName = node.getName(); + String ext = FilenameUtils.getExtension(fileName); + + // TODO use a more generic / standard approach + // see http://svn.apache.org/viewvc/tomcat/trunk/conf/web.xml + String contentType; + if ("xml".equals(ext)) + contentType = "text/xml;charset=" + contentTypeCharset; + else if ("jar".equals(ext)) + contentType = "application/java-archive"; + else if ("zip".equals(ext)) + contentType = "application/zip"; + else if ("gz".equals(ext)) + contentType = "application/x-gzip"; + else if ("bz2".equals(ext)) + contentType = "application/x-bzip2"; + else if ("tar".equals(ext)) + contentType = "application/x-tar"; + else if ("rpm".equals(ext)) + contentType = "application/x-redhat-package-manager"; + else + contentType = "application/octet-stream"; + contentType = contentType + ";name=\"" + fileName + "\""; + response.setHeader("Content-Disposition", "attachment; filename=\"" + + fileName + "\""); + response.setHeader("Expires", "0"); + response.setHeader("Cache-Control", "no-cache, must-revalidate"); + response.setHeader("Pragma", "no-cache"); + + response.setContentType(contentType); + + try { + binary = node.getNode(Property.JCR_CONTENT) + .getProperty(Property.JCR_DATA).getBinary(); + } catch (PathNotFoundException e) { + log.error("Node " + node + " as no data under content"); + throw e; + } + in = binary.getStream(); + IOUtils.copy(in, response.getOutputStream()); + } catch (Exception e) { + throw new ArgeoJcrException("Cannot download " + node, e); + } finally { + IOUtils.closeQuietly(in); + JcrUtils.closeQuietly(binary); + } + } + + public void setProxy(ResourceProxy resourceProxy) { + this.proxy = resourceProxy; + } + +} diff --git a/org.argeo.jcr/src/org/argeo/jcr/security/JcrAuthorizations.java b/org.argeo.jcr/src/org/argeo/jcr/security/JcrAuthorizations.java new file mode 100644 index 000000000..9ad249252 --- /dev/null +++ b/org.argeo.jcr/src/org/argeo/jcr/security/JcrAuthorizations.java @@ -0,0 +1,223 @@ +/* + * 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.jcr.security; + +import java.security.Principal; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import javax.jcr.Repository; +import javax.jcr.RepositoryException; +import javax.jcr.Session; +import javax.jcr.security.AccessControlManager; +import javax.jcr.security.Privilege; + +import org.argeo.jcr.ArgeoJcrException; +import org.argeo.jcr.JcrUtils; +import org.argeo.util.security.SimplePrincipal; + +/** Apply authorizations to a JCR repository. */ +public class JcrAuthorizations implements Runnable { + // private final static Log log = + // LogFactory.getLog(JcrAuthorizations.class); + + private Repository repository; + private String workspace = null; + + private String securityWorkspace = "security"; + + /** + * key := privilege1,privilege2/path/to/node
+ * value := group1,group2,user1 + */ + private Map principalPrivileges = new HashMap(); + + public void run() { + String currentWorkspace = workspace; + Session session = null; + try { + if (workspace != null && workspace.equals("*")) { + session = repository.login(); + String[] workspaces = session.getWorkspace() + .getAccessibleWorkspaceNames(); + JcrUtils.logoutQuietly(session); + for (String wksp : workspaces) { + currentWorkspace = wksp; + if (currentWorkspace.equals(securityWorkspace)) + continue; + session = repository.login(currentWorkspace); + initAuthorizations(session); + JcrUtils.logoutQuietly(session); + } + } else { + session = repository.login(workspace); + initAuthorizations(session); + } + } catch (Exception e) { + JcrUtils.discardQuietly(session); + throw new ArgeoJcrException( + "Cannot set authorizations " + principalPrivileges + + " on workspace " + currentWorkspace, e); + } finally { + JcrUtils.logoutQuietly(session); + } + } + + protected void processWorkspace(String workspace) { + Session session = null; + try { + session = repository.login(workspace); + initAuthorizations(session); + } catch (Exception e) { + JcrUtils.discardQuietly(session); + throw new ArgeoJcrException("Cannot set authorizations " + + principalPrivileges + " on repository " + repository, e); + } finally { + JcrUtils.logoutQuietly(session); + } + } + + /** @deprecated call {@link #run()} instead. */ + @Deprecated + public void init() { + run(); + } + + protected void initAuthorizations(Session session) + throws RepositoryException { + AccessControlManager acm = session.getAccessControlManager(); + + for (String privileges : principalPrivileges.keySet()) { + String path = null; + int slashIndex = privileges.indexOf('/'); + if (slashIndex == 0) { + throw new ArgeoJcrException("Privilege " + privileges + + " badly formatted it starts with /"); + } else if (slashIndex > 0) { + path = privileges.substring(slashIndex); + privileges = privileges.substring(0, slashIndex); + } + + if (path == null) + path = "/"; + + List privs = new ArrayList(); + for (String priv : privileges.split(",")) { + privs.add(acm.privilegeFromName(priv)); + } + + String principalNames = principalPrivileges.get(privileges); + for (String principalName : principalNames.split(",")) { + Principal principal = getOrCreatePrincipal(session, + principalName); + JcrUtils.addPrivileges(session, path, principal, privs); + // if (log.isDebugEnabled()) { + // StringBuffer privBuf = new StringBuffer(); + // for (Privilege priv : privs) + // privBuf.append(priv.getName()); + // log.debug("Added privileges " + privBuf + " to " + // + principal.getName() + " on " + path + " in '" + // + session.getWorkspace().getName() + "'"); + // } + } + } + + // if (log.isDebugEnabled()) + // log.debug("JCR authorizations applied on '" + // + session.getWorkspace().getName() + "'"); + } + + /** + * Returns a {@link SimplePrincipal}, does not check whether it exists since + * such capabilities is not provided by the standard JCR API. Can be + * overridden to provide smarter handling + */ + protected Principal getOrCreatePrincipal(Session session, + String principalName) throws RepositoryException { + return new SimplePrincipal(principalName); + } + + // public static void addPrivileges(Session session, Principal principal, + // String path, List privs) throws RepositoryException { + // AccessControlManager acm = session.getAccessControlManager(); + // // search for an access control list + // AccessControlList acl = null; + // AccessControlPolicyIterator policyIterator = acm + // .getApplicablePolicies(path); + // if (policyIterator.hasNext()) { + // while (policyIterator.hasNext()) { + // AccessControlPolicy acp = policyIterator + // .nextAccessControlPolicy(); + // if (acp instanceof AccessControlList) + // acl = ((AccessControlList) acp); + // } + // } else { + // AccessControlPolicy[] existingPolicies = acm.getPolicies(path); + // for (AccessControlPolicy acp : existingPolicies) { + // if (acp instanceof AccessControlList) + // acl = ((AccessControlList) acp); + // } + // } + // + // if (acl != null) { + // acl.addAccessControlEntry(principal, + // privs.toArray(new Privilege[privs.size()])); + // acm.setPolicy(path, acl); + // session.save(); + // if (log.isDebugEnabled()) { + // StringBuffer buf = new StringBuffer(""); + // for (int i = 0; i < privs.size(); i++) { + // if (i != 0) + // buf.append(','); + // buf.append(privs.get(i).getName()); + // } + // log.debug("Added privilege(s) '" + buf + "' to '" + // + principal.getName() + "' on " + path + // + " from workspace '" + // + session.getWorkspace().getName() + "'"); + // } + // } else { + // throw new ArgeoJcrException("Don't know how to apply privileges " + // + privs + " to " + principal + " on " + path + // + " from workspace '" + session.getWorkspace().getName() + // + "'"); + // } + // } + + @Deprecated + public void setGroupPrivileges(Map groupPrivileges) { + this.principalPrivileges = groupPrivileges; + } + + public void setPrincipalPrivileges(Map principalPrivileges) { + this.principalPrivileges = principalPrivileges; + } + + public void setRepository(Repository repository) { + this.repository = repository; + } + + public void setWorkspace(String workspace) { + this.workspace = workspace; + } + + public void setSecurityWorkspace(String securityWorkspace) { + this.securityWorkspace = securityWorkspace; + } + +} diff --git a/org.argeo.jcr/src/org/argeo/jcr/security/JcrKeyring.java b/org.argeo.jcr/src/org/argeo/jcr/security/JcrKeyring.java new file mode 100644 index 000000000..8ab6ed355 --- /dev/null +++ b/org.argeo.jcr/src/org/argeo/jcr/security/JcrKeyring.java @@ -0,0 +1,295 @@ +/* + * 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.jcr.security; + +import java.io.ByteArrayInputStream; +import java.io.CharArrayReader; +import java.io.InputStream; +import java.io.Reader; +import java.security.Provider; +import java.security.SecureRandom; + +import javax.crypto.Cipher; +import javax.crypto.CipherInputStream; +import javax.crypto.SecretKey; +import javax.crypto.spec.IvParameterSpec; +import javax.jcr.Binary; +import javax.jcr.Node; +import javax.jcr.Property; +import javax.jcr.RepositoryException; +import javax.jcr.Session; + +import org.apache.commons.io.IOUtils; +import org.argeo.jcr.ArgeoJcrException; +import org.argeo.jcr.ArgeoNames; +import org.argeo.jcr.ArgeoTypes; +import org.argeo.jcr.JcrUtils; +import org.argeo.jcr.UserJcrUtils; +import org.argeo.util.security.AbstractKeyring; +import org.argeo.util.security.PBEKeySpecCallback; + +/** JCR based implementation of a keyring */ +public class JcrKeyring extends AbstractKeyring implements ArgeoNames { + /** + * Stronger with 256, but causes problem with Oracle JVM, force 128 in this + * case + */ + public final static Long DEFAULT_SECRETE_KEY_LENGTH = 256l; + public final static String DEFAULT_SECRETE_KEY_FACTORY = "PBKDF2WithHmacSHA1"; + public final static String DEFAULT_SECRETE_KEY_ENCRYPTION = "AES"; + public final static String DEFAULT_CIPHER_NAME = "AES/CBC/PKCS5Padding"; + + private Integer iterationCountFactor = 200; + private Long secreteKeyLength = DEFAULT_SECRETE_KEY_LENGTH; + private String secreteKeyFactoryName = DEFAULT_SECRETE_KEY_FACTORY; + private String secreteKeyEncryption = DEFAULT_SECRETE_KEY_ENCRYPTION; + private String cipherName = DEFAULT_CIPHER_NAME; + + private Session session; + + /** + * When setup is called the session has not yet been saved and we don't want + * to save it since there maybe other data which would be inconsistent. So + * we keep a reference to this node which will then be used (an reset to + * null) when handling the PBE callback. We keep one per thread in case + * multiple users are accessing the same instance of a keyring. + */ + private ThreadLocal notYetSavedKeyring = new ThreadLocal() { + + @Override + protected Node initialValue() { + return null; + } + }; + + @Override + protected Boolean isSetup() { + try { + if (notYetSavedKeyring.get() != null) + return true; + + Node userHome = UserJcrUtils.getUserHome(session); + return userHome.hasNode(ARGEO_KEYRING); + } catch (RepositoryException e) { + throw new ArgeoJcrException("Cannot check whether keyring is setup", e); + } + } + + @Override + protected void setup(char[] password) { + Binary binary = null; + InputStream in = null; + try { + Node userHome = UserJcrUtils.getUserHome(session); + if (userHome.hasNode(ARGEO_KEYRING)) + throw new ArgeoJcrException("Keyring already setup"); + Node keyring = userHome.addNode(ARGEO_KEYRING); + keyring.addMixin(ArgeoTypes.ARGEO_PBE_SPEC); + + // deterministic salt and iteration count based on username + String username = session.getUserID(); + byte[] salt = new byte[8]; + byte[] usernameBytes = username.getBytes(); + for (int i = 0; i < salt.length; i++) { + if (i < usernameBytes.length) + salt[i] = usernameBytes[i]; + else + salt[i] = 0; + } + in = new ByteArrayInputStream(salt); + binary = session.getValueFactory().createBinary(in); + keyring.setProperty(ARGEO_SALT, binary); + + Integer iterationCount = username.length() * iterationCountFactor; + keyring.setProperty(ARGEO_ITERATION_COUNT, iterationCount); + + // default algo + // TODO check if algo and key length are available, use DES if not + keyring.setProperty(ARGEO_SECRET_KEY_FACTORY, secreteKeyFactoryName); + keyring.setProperty(ARGEO_KEY_LENGTH, secreteKeyLength); + keyring.setProperty(ARGEO_SECRET_KEY_ENCRYPTION, secreteKeyEncryption); + keyring.setProperty(ARGEO_CIPHER, cipherName); + + // keyring.getSession().save(); + + // encrypted password hash + // IOUtils.closeQuietly(in); + // JcrUtils.closeQuietly(binary); + // byte[] btPass = hash(password, salt, iterationCount); + // in = new ByteArrayInputStream(btPass); + // binary = session.getValueFactory().createBinary(in); + // keyring.setProperty(ARGEO_PASSWORD, binary); + + notYetSavedKeyring.set(keyring); + } catch (Exception e) { + throw new ArgeoJcrException("Cannot setup keyring", e); + } finally { + JcrUtils.closeQuietly(binary); + IOUtils.closeQuietly(in); + // JcrUtils.discardQuietly(session); + } + } + + @Override + protected void handleKeySpecCallback(PBEKeySpecCallback pbeCallback) { + try { + Node userHome = UserJcrUtils.getUserHome(session); + Node keyring; + if (userHome.hasNode(ARGEO_KEYRING)) + keyring = userHome.getNode(ARGEO_KEYRING); + else if (notYetSavedKeyring.get() != null) + keyring = notYetSavedKeyring.get(); + else + throw new ArgeoJcrException("Keyring not setup"); + + pbeCallback.set(keyring.getProperty(ARGEO_SECRET_KEY_FACTORY).getString(), + JcrUtils.getBinaryAsBytes(keyring.getProperty(ARGEO_SALT)), + (int) keyring.getProperty(ARGEO_ITERATION_COUNT).getLong(), + (int) keyring.getProperty(ARGEO_KEY_LENGTH).getLong(), + keyring.getProperty(ARGEO_SECRET_KEY_ENCRYPTION).getString()); + + if (notYetSavedKeyring.get() != null) + notYetSavedKeyring.remove(); + } catch (RepositoryException e) { + throw new ArgeoJcrException("Cannot handle key spec callback", e); + } + } + + /** The parent node must already exist at this path. */ + @Override + protected synchronized void encrypt(String path, InputStream unencrypted) { + // should be called first for lazy initialization + SecretKey secretKey = getSecretKey(); + + Binary binary = null; + InputStream in = null; + try { + Cipher cipher = createCipher(); + Node node; + if (!session.nodeExists(path)) { + String parentPath = JcrUtils.parentPath(path); + if (!session.nodeExists(parentPath)) + throw new ArgeoJcrException("No parent node of " + path); + Node parentNode = session.getNode(parentPath); + node = parentNode.addNode(JcrUtils.nodeNameFromPath(path)); + } else { + node = session.getNode(path); + } + node.addMixin(ArgeoTypes.ARGEO_ENCRYPTED); + SecureRandom random = new SecureRandom(); + byte[] iv = new byte[16]; + random.nextBytes(iv); + cipher.init(Cipher.ENCRYPT_MODE, secretKey, new IvParameterSpec(iv)); + JcrUtils.setBinaryAsBytes(node, ARGEO_IV, iv); + + in = new CipherInputStream(unencrypted, cipher); + binary = session.getValueFactory().createBinary(in); + node.setProperty(Property.JCR_DATA, binary); + session.save(); + } catch (Exception e) { + throw new ArgeoJcrException("Cannot encrypt", e); + } finally { + IOUtils.closeQuietly(unencrypted); + IOUtils.closeQuietly(in); + JcrUtils.closeQuietly(binary); + } + } + + @Override + protected synchronized InputStream decrypt(String path) { + Binary binary = null; + InputStream encrypted = null; + Reader reader = null; + try { + if (!session.nodeExists(path)) { + char[] password = ask(); + reader = new CharArrayReader(password); + return new ByteArrayInputStream(IOUtils.toByteArray(reader)); + } else { + // should be called first for lazy initialisation + SecretKey secretKey = getSecretKey(); + + Cipher cipher = createCipher(); + + Node node = session.getNode(path); + if (node.hasProperty(ARGEO_IV)) { + byte[] iv = JcrUtils.getBinaryAsBytes(node.getProperty(ARGEO_IV)); + cipher.init(Cipher.DECRYPT_MODE, secretKey, new IvParameterSpec(iv)); + } else { + cipher.init(Cipher.DECRYPT_MODE, secretKey); + } + + binary = node.getProperty(Property.JCR_DATA).getBinary(); + encrypted = binary.getStream(); + return new CipherInputStream(encrypted, cipher); + } + } catch (Exception e) { + throw new ArgeoJcrException("Cannot decrypt", e); + } finally { + IOUtils.closeQuietly(encrypted); + IOUtils.closeQuietly(reader); + JcrUtils.closeQuietly(binary); + } + } + + protected Cipher createCipher() { + try { + Node userHome = UserJcrUtils.getUserHome(session); + if (!userHome.hasNode(ARGEO_KEYRING)) + throw new ArgeoJcrException("Keyring not setup"); + Node keyring = userHome.getNode(ARGEO_KEYRING); + String cipherName = keyring.getProperty(ARGEO_CIPHER).getString(); + Provider securityProvider = getSecurityProvider(); + Cipher cipher; + if (securityProvider == null)// TODO use BC? + cipher = Cipher.getInstance(cipherName); + else + cipher = Cipher.getInstance(cipherName, securityProvider); + return cipher; + } catch (Exception e) { + throw new ArgeoJcrException("Cannot get cipher", e); + } + } + + public synchronized void changePassword(char[] oldPassword, char[] newPassword) { + // TODO decrypt with old pw / encrypt with new pw all argeo:encrypted + } + + public synchronized void setSession(Session session) { + this.session = session; + } + + public void setIterationCountFactor(Integer iterationCountFactor) { + this.iterationCountFactor = iterationCountFactor; + } + + public void setSecreteKeyLength(Long keyLength) { + this.secreteKeyLength = keyLength; + } + + public void setSecreteKeyFactoryName(String secreteKeyFactoryName) { + this.secreteKeyFactoryName = secreteKeyFactoryName; + } + + public void setSecreteKeyEncryption(String secreteKeyEncryption) { + this.secreteKeyEncryption = secreteKeyEncryption; + } + + public void setCipherName(String cipherName) { + this.cipherName = cipherName; + } + +} \ No newline at end of file diff --git a/org.argeo.jcr/src/org/argeo/jcr/spring/ThreadBoundSession.java b/org.argeo.jcr/src/org/argeo/jcr/spring/ThreadBoundSession.java new file mode 100644 index 000000000..35f021555 --- /dev/null +++ b/org.argeo.jcr/src/org/argeo/jcr/spring/ThreadBoundSession.java @@ -0,0 +1,34 @@ +/* + * 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.jcr.spring; + +import org.argeo.jcr.ThreadBoundJcrSessionFactory; +import org.springframework.beans.factory.DisposableBean; +import org.springframework.beans.factory.FactoryBean; +import org.springframework.beans.factory.InitializingBean; + +@SuppressWarnings("rawtypes") +@Deprecated +public class ThreadBoundSession extends ThreadBoundJcrSessionFactory implements FactoryBean, InitializingBean, DisposableBean{ + public void afterPropertiesSet() throws Exception { + init(); + } + + public void destroy() throws Exception { + dispose(); + } + +} diff --git a/org.argeo.jcr/src/org/argeo/jcr/tabular/JcrTabularRowIterator.java b/org.argeo.jcr/src/org/argeo/jcr/tabular/JcrTabularRowIterator.java new file mode 100644 index 000000000..d4ffbf8c2 --- /dev/null +++ b/org.argeo.jcr/src/org/argeo/jcr/tabular/JcrTabularRowIterator.java @@ -0,0 +1,185 @@ +/* + * 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.jcr.tabular; + +import java.io.InputStream; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.ArrayBlockingQueue; + +import javax.jcr.Binary; +import javax.jcr.Node; +import javax.jcr.NodeIterator; +import javax.jcr.Property; +import javax.jcr.PropertyType; +import javax.jcr.RepositoryException; + +import org.apache.commons.io.IOUtils; +import org.argeo.jcr.ArgeoJcrException; +import org.argeo.jcr.ArgeoTypes; +import org.argeo.util.CsvParser; +import org.argeo.util.tabular.ArrayTabularRow; +import org.argeo.util.tabular.TabularColumn; +import org.argeo.util.tabular.TabularRow; +import org.argeo.util.tabular.TabularRowIterator; + +/** Iterates over the rows of a {@link ArgeoTypes#ARGEO_TABLE} node. */ +public class JcrTabularRowIterator implements TabularRowIterator { + private Boolean hasNext = null; + private Boolean parsingCompleted = false; + + private Long currentRowNumber = 0l; + + private List header = new ArrayList(); + + /** referenced so that we can close it */ + private Binary binary; + private InputStream in; + + private CsvParser csvParser; + private ArrayBlockingQueue> textLines; + + public JcrTabularRowIterator(Node tableNode) { + try { + for (NodeIterator it = tableNode.getNodes(); it.hasNext();) { + Node node = it.nextNode(); + if (node.isNodeType(ArgeoTypes.ARGEO_COLUMN)) { + Integer type = PropertyType.valueFromName(node.getProperty( + Property.JCR_REQUIRED_TYPE).getString()); + TabularColumn tc = new TabularColumn(node.getProperty( + Property.JCR_TITLE).getString(), type); + header.add(tc); + } + } + Node contentNode = tableNode.getNode(Property.JCR_CONTENT); + if (contentNode.isNodeType(ArgeoTypes.ARGEO_CSV)) { + textLines = new ArrayBlockingQueue>(1000); + csvParser = new CsvParser() { + protected void processLine(Integer lineNumber, + List header, List tokens) { + try { + textLines.put(tokens); + } catch (InterruptedException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + // textLines.add(tokens); + if (hasNext == null) { + hasNext = true; + synchronized (JcrTabularRowIterator.this) { + JcrTabularRowIterator.this.notifyAll(); + } + } + } + }; + csvParser.setNoHeader(true); + binary = contentNode.getProperty(Property.JCR_DATA).getBinary(); + in = binary.getStream(); + Thread thread = new Thread(contentNode.getPath() + " reader") { + public void run() { + try { + csvParser.parse(in); + } finally { + parsingCompleted = true; + IOUtils.closeQuietly(in); + } + } + }; + thread.start(); + } + } catch (RepositoryException e) { + throw new ArgeoJcrException("Cannot read table " + tableNode, e); + } + } + + public synchronized boolean hasNext() { + // we don't know if there is anything available + // while (hasNext == null) + // try { + // wait(); + // } catch (InterruptedException e) { + // // silent + // // FIXME better deal with interruption + // Thread.currentThread().interrupt(); + // break; + // } + + // buffer not empty + if (!textLines.isEmpty()) + return true; + + // maybe the parsing is finished but the flag has not been set + while (!parsingCompleted && textLines.isEmpty()) + try { + wait(100); + } catch (InterruptedException e) { + // silent + // FIXME better deal with interruption + Thread.currentThread().interrupt(); + break; + } + + // buffer not empty + if (!textLines.isEmpty()) + return true; + + // (parsingCompleted && textLines.isEmpty()) + return false; + + // if (!hasNext && textLines.isEmpty()) { + // if (in != null) { + // IOUtils.closeQuietly(in); + // in = null; + // } + // if (binary != null) { + // JcrUtils.closeQuietly(binary); + // binary = null; + // } + // return false; + // } else + // return true; + } + + public synchronized TabularRow next() { + try { + List tokens = textLines.take(); + List objs = new ArrayList(tokens.size()); + for (String token : tokens) { + // TODO convert to other formats using header + objs.add(token); + } + currentRowNumber++; + return new ArrayTabularRow(objs); + } catch (InterruptedException e) { + // silent + // FIXME better deal with interruption + } + return null; + } + + public void remove() { + throw new UnsupportedOperationException(); + } + + public Long getCurrentRowNumber() { + return currentRowNumber; + } + + public List getHeader() { + return header; + } + +} diff --git a/org.argeo.jcr/src/org/argeo/jcr/tabular/JcrTabularWriter.java b/org.argeo.jcr/src/org/argeo/jcr/tabular/JcrTabularWriter.java new file mode 100644 index 000000000..c3fd97cbd --- /dev/null +++ b/org.argeo.jcr/src/org/argeo/jcr/tabular/JcrTabularWriter.java @@ -0,0 +1,97 @@ +/* + * 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.jcr.tabular; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.InputStream; +import java.util.List; + +import javax.jcr.Binary; +import javax.jcr.Node; +import javax.jcr.Property; +import javax.jcr.PropertyType; +import javax.jcr.RepositoryException; + +import org.apache.commons.io.IOUtils; +import org.argeo.jcr.ArgeoJcrException; +import org.argeo.jcr.ArgeoTypes; +import org.argeo.jcr.JcrUtils; +import org.argeo.util.CsvWriter; +import org.argeo.util.tabular.TabularColumn; +import org.argeo.util.tabular.TabularWriter; + +/** Write / reference tabular content in a JCR repository. */ +public class JcrTabularWriter implements TabularWriter { + private Node contentNode; + private ByteArrayOutputStream out; + private CsvWriter csvWriter; + + @SuppressWarnings("unused") + private final List columns; + + /** Creates a table node */ + public JcrTabularWriter(Node tableNode, List columns, + String contentNodeType) { + try { + this.columns = columns; + for (TabularColumn column : columns) { + String normalized = JcrUtils.replaceInvalidChars(column + .getName()); + Node columnNode = tableNode.addNode(normalized, + ArgeoTypes.ARGEO_COLUMN); + columnNode.setProperty(Property.JCR_TITLE, column.getName()); + if (column.getType() != null) + columnNode.setProperty(Property.JCR_REQUIRED_TYPE, + PropertyType.nameFromValue(column.getType())); + else + columnNode.setProperty(Property.JCR_REQUIRED_TYPE, + PropertyType.TYPENAME_STRING); + } + contentNode = tableNode.addNode(Property.JCR_CONTENT, + contentNodeType); + if (contentNodeType.equals(ArgeoTypes.ARGEO_CSV)) { + contentNode.setProperty(Property.JCR_MIMETYPE, "text/csv"); + contentNode.setProperty(Property.JCR_ENCODING, "UTF-8"); + out = new ByteArrayOutputStream(); + csvWriter = new CsvWriter(out); + } + } catch (RepositoryException e) { + throw new ArgeoJcrException("Cannot create table node " + tableNode, e); + } + } + + public void appendRow(Object[] row) { + csvWriter.writeLine(row); + } + + public void close() { + Binary binary = null; + InputStream in = null; + try { + // TODO parallelize with pipes and writing from another thread + in = new ByteArrayInputStream(out.toByteArray()); + binary = contentNode.getSession().getValueFactory() + .createBinary(in); + contentNode.setProperty(Property.JCR_DATA, binary); + } catch (RepositoryException e) { + throw new ArgeoJcrException("Cannot store data in " + contentNode, e); + } finally { + IOUtils.closeQuietly(in); + JcrUtils.closeQuietly(binary); + } + } +} diff --git a/org.argeo.jcr/src/org/argeo/jcr/unit/AbstractJcrTestCase.java b/org.argeo.jcr/src/org/argeo/jcr/unit/AbstractJcrTestCase.java new file mode 100644 index 000000000..1269a3ee5 --- /dev/null +++ b/org.argeo.jcr/src/org/argeo/jcr/unit/AbstractJcrTestCase.java @@ -0,0 +1,133 @@ +/* + * 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.jcr.unit; + +import java.io.File; +import java.security.AccessController; +import java.security.PrivilegedAction; + +import javax.jcr.Repository; +import javax.jcr.Session; +import javax.jcr.SimpleCredentials; +import javax.security.auth.Subject; +import javax.security.auth.login.LoginContext; +import javax.security.auth.login.LoginException; + +import org.apache.commons.io.FileUtils; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.argeo.jcr.ArgeoJcrException; + +import junit.framework.TestCase; + +public abstract class AbstractJcrTestCase extends TestCase { + private final static Log log = LogFactory.getLog(AbstractJcrTestCase.class); + + private Repository repository; + private Session session = null; + + public final static String LOGIN_CONTEXT_TEST_SYSTEM = "TEST_JACKRABBIT_ADMIN"; + + // protected abstract File getRepositoryFile() throws Exception; + + protected abstract Repository createRepository() throws Exception; + + protected abstract void clearRepository(Repository repository) + throws Exception; + + @Override + protected void setUp() throws Exception { + File homeDir = getHomeDir(); + FileUtils.deleteDirectory(homeDir); + repository = createRepository(); + } + + @Override + protected void tearDown() throws Exception { + if (session != null) { + session.logout(); + if (log.isTraceEnabled()) + log.trace("Logout session"); + } + clearRepository(repository); + } + + protected Session session() { + if (session != null && session.isLive()) + return session; + Session session; + if (getLoginContext() != null) { + LoginContext lc; + try { + lc = new LoginContext(getLoginContext()); + lc.login(); + } catch (LoginException e) { + throw new ArgeoJcrException("JAAS login failed", e); + } + session = Subject.doAs(lc.getSubject(), + new PrivilegedAction() { + + @Override + public Session run() { + return login(); + } + + }); + } else + session = login(); + this.session = session; + return this.session; + } + + protected String getLoginContext() { + return null; + } + + protected Session login() { + try { + if (log.isTraceEnabled()) + log.trace("Login session"); + Subject subject = Subject.getSubject(AccessController.getContext()); + if (subject != null) + return getRepository().login(); + else + return getRepository().login( + new SimpleCredentials("demo", "demo".toCharArray())); + } catch (Exception e) { + throw new ArgeoJcrException("Cannot login to repository", e); + } + } + + protected Repository getRepository() { + return repository; + } + + /** + * enables children class to set an existing repository in case it is not + * deleted on startup, to test migration by instance + */ + public void setRepository(Repository repository) { + this.repository = repository; + } + + protected File getHomeDir() { + File homeDir = new File(System.getProperty("java.io.tmpdir"), + AbstractJcrTestCase.class.getSimpleName() + "-" + + System.getProperty("user.name")); + return homeDir; + } + +} diff --git a/org.argeo.node.api/.classpath b/org.argeo.node.api/.classpath new file mode 100644 index 000000000..eca7bdba8 --- /dev/null +++ b/org.argeo.node.api/.classpath @@ -0,0 +1,7 @@ + + + + + + + diff --git a/org.argeo.node.api/.project b/org.argeo.node.api/.project new file mode 100644 index 000000000..9573f0c01 --- /dev/null +++ b/org.argeo.node.api/.project @@ -0,0 +1,28 @@ + + + org.argeo.node.api + + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.pde.ManifestBuilder + + + + + org.eclipse.pde.SchemaBuilder + + + + + + org.eclipse.pde.PluginNature + org.eclipse.jdt.core.javanature + + diff --git a/org.argeo.node.api/bnd.bnd b/org.argeo.node.api/bnd.bnd new file mode 100644 index 000000000..52a2bdd57 --- /dev/null +++ b/org.argeo.node.api/bnd.bnd @@ -0,0 +1 @@ +Provide-Capability: cms.datamodel;name=node;cnd=/org/argeo/node/node.cnd \ No newline at end of file diff --git a/org.argeo.node.api/build.properties b/org.argeo.node.api/build.properties new file mode 100644 index 000000000..34d2e4d2d --- /dev/null +++ b/org.argeo.node.api/build.properties @@ -0,0 +1,4 @@ +source.. = src/ +output.. = bin/ +bin.includes = META-INF/,\ + . diff --git a/org.argeo.node.api/pom.xml b/org.argeo.node.api/pom.xml new file mode 100644 index 000000000..c52d9a489 --- /dev/null +++ b/org.argeo.node.api/pom.xml @@ -0,0 +1,15 @@ + + + 4.0.0 + + org.argeo.commons + argeo-commons + 2.1.46-SNAPSHOT + .. + + org.argeo.node.api + Argeo Node API + jar + + + \ No newline at end of file diff --git a/org.argeo.node.api/src/org/argeo/node/ArgeoLogListener.java b/org.argeo.node.api/src/org/argeo/node/ArgeoLogListener.java new file mode 100644 index 000000000..698dfe1be --- /dev/null +++ b/org.argeo.node.api/src/org/argeo/node/ArgeoLogListener.java @@ -0,0 +1,39 @@ +/* + * 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.node; + +/** Framework agnostic interface for log notifications */ +public interface ArgeoLogListener { + /** + * Appends a log + * + * @param username + * authentified user, null for anonymous + * @param level + * INFO, DEBUG, WARN, etc. (logging framework specific) + * @param category + * hierarchy (logging framework specific) + * @param thread + * name of the thread which logged this message + * @param msg + * any object as long as its toString() method returns the + * message + * @param the + * exception in log4j ThrowableStrRep format + */ + public void appendLog(String username, Long timestamp, String level, + String category, String thread, Object msg, String[] exception); +} diff --git a/org.argeo.node.api/src/org/argeo/node/ArgeoLogger.java b/org.argeo.node.api/src/org/argeo/node/ArgeoLogger.java new file mode 100644 index 000000000..213286d44 --- /dev/null +++ b/org.argeo.node.api/src/org/argeo/node/ArgeoLogger.java @@ -0,0 +1,46 @@ +/* + * 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.node; + +/** + * Logging framework agnostic identifying a logging service, to which one can + * register + */ +public interface ArgeoLogger { + /** + * Register for events by threads with the same authentication (or all + * threads if admin) + */ + public void register(ArgeoLogListener listener, + Integer numberOfPreviousEvents); + + /** + * For admin use only: register for all users + * + * @param listener + * the log listener + * @param numberOfPreviousEvents + * the number of previous events to notify + * @param everything + * if true even anonymous is logged + */ + public void registerForAll(ArgeoLogListener listener, + Integer numberOfPreviousEvents, boolean everything); + + public void unregister(ArgeoLogListener listener); + + public void unregisterForAll(ArgeoLogListener listener); +} diff --git a/org.argeo.node.api/src/org/argeo/node/DataAdminPrincipal.java b/org.argeo.node.api/src/org/argeo/node/DataAdminPrincipal.java new file mode 100644 index 000000000..743c96f2e --- /dev/null +++ b/org.argeo.node.api/src/org/argeo/node/DataAdminPrincipal.java @@ -0,0 +1,30 @@ +package org.argeo.node; + +import java.security.Principal; + +/** Allows to modify any data. */ +public final class DataAdminPrincipal implements Principal { + // FIXME put auth constants in API + private final String name = "OU=node"; + + @Override + public String getName() { + return name; + } + + @Override + public int hashCode() { + return name.hashCode(); + } + + @Override + public boolean equals(Object obj) { + return this == obj; + } + + @Override + public String toString() { + return name.toString(); + } + +} diff --git a/org.argeo.node.api/src/org/argeo/node/DataModelNamespace.java b/org.argeo.node.api/src/org/argeo/node/DataModelNamespace.java new file mode 100644 index 000000000..578419609 --- /dev/null +++ b/org.argeo.node.api/src/org/argeo/node/DataModelNamespace.java @@ -0,0 +1,16 @@ +package org.argeo.node; + +import org.osgi.resource.Namespace; + +/** CMS Data Model capability namespace. */ +public class DataModelNamespace extends Namespace { + + public static final String CMS_DATA_MODEL_NAMESPACE = "cms.datamodel"; + public static final String CAPABILITY_NAME_ATTRIBUTE = "name"; + public static final String CAPABILITY_CND_ATTRIBUTE = "cnd"; + + private DataModelNamespace() { + // empty + } + +} diff --git a/org.argeo.node.api/src/org/argeo/node/EnumAD.java b/org.argeo.node.api/src/org/argeo/node/EnumAD.java new file mode 100644 index 000000000..1ee6d39f0 --- /dev/null +++ b/org.argeo.node.api/src/org/argeo/node/EnumAD.java @@ -0,0 +1,59 @@ +package org.argeo.node; + +import org.osgi.service.metatype.AttributeDefinition; + +interface EnumAD extends AttributeDefinition { + String name(); + + default Object getDefault() { + return null; + } + + @Override + default String getName() { + return name(); + } + + @Override + default String getID() { + return getClass().getName() + "." + name(); + } + + @Override + default String getDescription() { + return null; + } + + @Override + default int getCardinality() { + return 0; + } + + @Override + default int getType() { + return STRING; + } + + @Override + default String[] getOptionValues() { + return null; + } + + @Override + default String[] getOptionLabels() { + return null; + } + + @Override + default String validate(String value) { + return null; + } + + @Override + default String[] getDefaultValue() { + Object value = getDefault(); + if (value == null) + return null; + return new String[] { value.toString() }; + } +} diff --git a/org.argeo.node.api/src/org/argeo/node/EnumOCD.java b/org.argeo.node.api/src/org/argeo/node/EnumOCD.java new file mode 100644 index 000000000..daa2f540d --- /dev/null +++ b/org.argeo.node.api/src/org/argeo/node/EnumOCD.java @@ -0,0 +1,50 @@ +package org.argeo.node; + +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.EnumSet; +import java.util.List; + +import org.osgi.service.metatype.AttributeDefinition; +import org.osgi.service.metatype.ObjectClassDefinition; + +class EnumOCD> implements ObjectClassDefinition { + private final Class enumClass; + private String locale; + + public EnumOCD(Class clazz, String locale) { + this.enumClass = clazz; + this.locale = locale; + } + + @Override + public String getName() { + return null; + } + + @Override + public String getID() { + return enumClass.getName(); + } + + @Override + public String getDescription() { + return null; + } + + @Override + public AttributeDefinition[] getAttributeDefinitions(int filter) { + EnumSet set = EnumSet.allOf(enumClass); + List attrs = new ArrayList<>(); + for (T key : set) + attrs.add((AttributeDefinition) key); + return attrs.toArray(new AttributeDefinition[attrs.size()]); + } + + @Override + public InputStream getIcon(int size) throws IOException { + return null; + } + +} diff --git a/org.argeo.node.api/src/org/argeo/node/NodeConstants.java b/org.argeo.node.api/src/org/argeo/node/NodeConstants.java new file mode 100644 index 000000000..477b0b96b --- /dev/null +++ b/org.argeo.node.api/src/org/argeo/node/NodeConstants.java @@ -0,0 +1,50 @@ +package org.argeo.node; + +public interface NodeConstants { + /* + * PIDs + */ + String NODE_STATE_PID = "org.argeo.node.state"; + String NODE_DEPLOYMENT_PID = "org.argeo.node.deployment"; + String NODE_INSTANCE_PID = "org.argeo.node.instance"; + +// String NODE_REPO_PID = "org.argeo.node.repo"; + String NODE_USER_ADMIN_PID = "org.argeo.node.userAdmin"; + + /* + * FACTORY PIDs + */ + String NODE_REPOS_FACTORY_PID = "org.argeo.node.repos"; + String NODE_USER_DIRECTORIES_FACTORY_PID = "org.argeo.node.userDirectories"; + + /* + * DEPLOY + */ + String DEPLOY_BASEDN = "ou=deploy,ou=node"; + + /* + * FRAMEWORK PROPERTIES + */ + String NODE_INIT = "argeo.node.init"; + String I18N_DEFAULT_LOCALE = "argeo.i18n.defaultLocale"; + String I18N_LOCALES = "argeo.i18n.locales"; + // Node Security + String ROLES_URI = "argeo.node.roles.uri"; + /** URI to an LDIF file or LDAP server used as initialization or backend */ + String USERADMIN_URIS = "argeo.node.useradmin.uris"; + // Node + /** Properties configuring the node repository */ + String NODE_REPO_PROP_PREFIX = "argeo.node.repo."; + + /* + * STANDARD ATTRIBUTES + */ + String CN = "cn"; + String OU = "ou"; + String LABELED_URI = "labeledUri"; + + /* + * STANDARD VALUES + */ + String DEFAULT = "default"; +} diff --git a/org.argeo.node.api/src/org/argeo/node/NodeDeployment.java b/org.argeo.node.api/src/org/argeo/node/NodeDeployment.java new file mode 100644 index 000000000..8e5558d60 --- /dev/null +++ b/org.argeo.node.api/src/org/argeo/node/NodeDeployment.java @@ -0,0 +1,5 @@ +package org.argeo.node; + +public interface NodeDeployment { + Long getAvailableSince(); +} diff --git a/org.argeo.node.api/src/org/argeo/node/NodeInstance.java b/org.argeo.node.api/src/org/argeo/node/NodeInstance.java new file mode 100644 index 000000000..e1e4bcdfa --- /dev/null +++ b/org.argeo.node.api/src/org/argeo/node/NodeInstance.java @@ -0,0 +1,5 @@ +package org.argeo.node; + +public interface NodeInstance { + +} diff --git a/org.argeo.node.api/src/org/argeo/node/NodeOID.java b/org.argeo.node.api/src/org/argeo/node/NodeOID.java new file mode 100644 index 000000000..9d8ff3d3a --- /dev/null +++ b/org.argeo.node.api/src/org/argeo/node/NodeOID.java @@ -0,0 +1,18 @@ +package org.argeo.node; + +interface NodeOID { + String BASE = "1.3.6.1.4.1" + ".48308" + ".1"; + + // ATTRIBUTE TYPES + String ATTRIBUTE_TYPES = BASE + ".4"; + String URI = ATTRIBUTE_TYPES + ".1"; + String HTTP_PORT = ATTRIBUTE_TYPES + ".2"; + String HTTPS_PORT = ATTRIBUTE_TYPES + ".3"; + + // OBJECT CLASSES + String OBJECT_CLASSES = BASE + ".6"; + String JCR_REPOSITORY = OBJECT_CLASSES + ".1"; + + // EXTERNAL + String LABELED_URI = "1.3.6.1.4.1.250.1.57"; +} diff --git a/org.argeo.node.api/src/org/argeo/node/NodeState.java b/org.argeo.node.api/src/org/argeo/node/NodeState.java new file mode 100644 index 000000000..d7148c68f --- /dev/null +++ b/org.argeo.node.api/src/org/argeo/node/NodeState.java @@ -0,0 +1,17 @@ +package org.argeo.node; + +import java.util.List; +import java.util.Locale; + +public interface NodeState { + Locale getDefaultLocale(); + + List getLocales(); + + String getHostname(); + + boolean isClean(); + + Long getAvailableSince(); + +} diff --git a/org.argeo.node.api/src/org/argeo/node/RepoConf.java b/org.argeo.node.api/src/org/argeo/node/RepoConf.java new file mode 100644 index 000000000..be4f6f7f7 --- /dev/null +++ b/org.argeo.node.api/src/org/argeo/node/RepoConf.java @@ -0,0 +1,66 @@ +package org.argeo.node; + +/** JCR repository configuration */ +public enum RepoConf implements EnumAD { + /** Repository type */ + type("localfs"), + /** Default workspace */ + @Deprecated + defaultWorkspace("main"), + /** Database URL */ + dburl(null), + /** Database user */ + dbuser(null), + /** Database password */ + dbpassword(null), + + /** The identifier (can be an URL locating the repo) */ + labeledUri(null), + // + // JACKRABBIT SPECIFIC + // + /** Maximum database pool size */ + maxPoolSize(10), + /** Maximum cache size in MB */ + @Deprecated + maxCacheMB(null), + /** Bundle cache size in MB */ + bundleCacheMB(8), + /** Extractor pool size */ + extractorPoolSize(0), + /** Search cache size */ + searchCacheSize(1000), + /** Max volatile index size */ + maxVolatileIndexSize(1048576); + + /** The default value. */ + private Object def; + private String oid; + + RepoConf(String oid, Object def) { + this.oid = oid; + this.def = def; + } + + RepoConf(Object def) { + this.def = def; + } + + public Object getDefault() { + return def; + } + + @Override + public String getID() { + if (oid != null) + return oid; + return EnumAD.super.getID(); + } + + public static class OCD extends EnumOCD { + public OCD(String locale) { + super(RepoConf.class, locale); + } + } + +} diff --git a/org.argeo.node.api/src/org/argeo/node/node.cnd b/org.argeo.node.api/src/org/argeo/node/node.cnd new file mode 100644 index 000000000..fbfea9dd9 --- /dev/null +++ b/org.argeo.node.api/src/org/argeo/node/node.cnd @@ -0,0 +1,72 @@ + + +// GENERIC TYPES NOT AVAILABLE IN JCR +[argeo:link] > mix:created, mix:lastModified +mixin +// URI(s) +- argeo:uri (STRING) m + +[argeo:references] > nt:unstructured +- * (REFERENCE) * + +// DATA MODEL +[argeo:dataModel] > mix:created, mix:lastModified, mix:versionable +mixin +- argeo:uri (STRING) m +- argeo:dataModelVersion (STRING) m + +// USER NODES +// user should be lower case, between 3 and 15 characters long +[argeo:userHome] > mix:created, mix:lastModified +mixin +- argeo:userID (STRING) m +- argeo:remoteRoles (STRING) * +// deprecated. for backward compatibility: ++ argeo:profile (argeo:userProfile) ++ argeo:keyring (argeo:pbeSpec) ++ argeo:preferences (argeo:preferenceNode) + +[argeo:userProfile] > mix:created, mix:lastModified, mix:title, mix:versionable +mixin +- argeo:userID (STRING) m +- argeo:enabled (BOOLEAN) +- argeo:accountNonExpired (BOOLEAN) +- argeo:accountNonLocked (BOOLEAN) +- argeo:credentialsNonExpired (BOOLEAN) + +[argeo:preferenceNode] > mix:lastModified, mix:versionable +mixin ++ * (argeo:preferenceNode) * version + +[argeo:remoteRepository] > nt:unstructured +- argeo:uri (STRING) +- argeo:userID (STRING) ++ argeo:password (argeo:encrypted) + +// TABULAR CONTENT +[argeo:table] > nt:file ++ * (argeo:column) * + +[argeo:column] > mix:title +- jcr:requiredType (STRING) = 'STRING' + +[argeo:csv] > nt:resource + +// CRYPTO +[argeo:encrypted] > nt:base +mixin +// initialization vector used by some algorithms +- argeo:iv (BINARY) + +[argeo:pbeKeySpec] > nt:base +mixin +- argeo:secretKeyFactory (STRING) +- argeo:salt (BINARY) +- argeo:iterationCount (LONG) +- argeo:keyLength (LONG) +- argeo:secretKeyEncryption (STRING) + +[argeo:pbeSpec] > argeo:pbeKeySpec +mixin +- argeo:cipher (STRING) + diff --git a/org.argeo.node.api/src/org/argeo/node/package-info.java b/org.argeo.node.api/src/org/argeo/node/package-info.java new file mode 100644 index 000000000..fda3baec5 --- /dev/null +++ b/org.argeo.node.api/src/org/argeo/node/package-info.java @@ -0,0 +1,5 @@ +/** + * Abstractions or constants related to an Argeo Node, an active repository of + * linked data. + */ +package org.argeo.node; \ No newline at end of file diff --git a/org.argeo.node.api/src/org/argeo/node/packageinfo b/org.argeo.node.api/src/org/argeo/node/packageinfo new file mode 100644 index 000000000..2c9afe82e --- /dev/null +++ b/org.argeo.node.api/src/org/argeo/node/packageinfo @@ -0,0 +1 @@ +version 2.1.0 \ No newline at end of file diff --git a/org.argeo.osgi.boot/src/org/argeo/osgi/boot/OsgiBoot.java b/org.argeo.osgi.boot/src/org/argeo/osgi/boot/OsgiBoot.java index 607f6264b..64ace0c3e 100644 --- a/org.argeo.osgi.boot/src/org/argeo/osgi/boot/OsgiBoot.java +++ b/org.argeo.osgi.boot/src/org/argeo/osgi/boot/OsgiBoot.java @@ -205,10 +205,13 @@ public class OsgiBoot implements OsgiBootConstants { warn("Skip " + url); return; } else { - Bundle bundle = bundleContext.installBundle(url); - OsgiBootUtils - .info("Installed " + bundle.getSymbolicName() + "-" + bundle.getVersion() + " from " + url); + if (url.startsWith("http")) + OsgiBootUtils + .info("Installed " + bundle.getSymbolicName() + "-" + bundle.getVersion() + " from " + url); + else if (debug) + OsgiBootUtils.debug( + "Installed " + bundle.getSymbolicName() + "-" + bundle.getVersion() + " from " + url); } } catch (BundleException e) { String message = e.getMessage(); @@ -642,14 +645,14 @@ public class OsgiBoot implements OsgiBootConstants { return (basePath + '/' + relativePath).replace('/', File.separatorChar); } - private String removeFilePrefix(String url) { - if (url.startsWith("file:")) - return url.substring("file:".length()); - else if (url.startsWith("reference:file:")) - return url.substring("reference:file:".length()); - else - return url; - } + // private String removeFilePrefix(String url) { + // if (url.startsWith("file:")) + // return url.substring("file:".length()); + // else if (url.startsWith("reference:file:")) + // return url.substring("reference:file:".length()); + // else + // return url; + // } /* * BEAN METHODS diff --git a/org.argeo.security.core/.classpath b/org.argeo.security.core/.classpath deleted file mode 100644 index 4e5da1da6..000000000 --- a/org.argeo.security.core/.classpath +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - diff --git a/org.argeo.security.core/.project b/org.argeo.security.core/.project deleted file mode 100644 index a8e385a9c..000000000 --- a/org.argeo.security.core/.project +++ /dev/null @@ -1,28 +0,0 @@ - - - org.argeo.security.core - - - - - - org.eclipse.jdt.core.javabuilder - - - - - org.eclipse.pde.ManifestBuilder - - - - - org.eclipse.pde.SchemaBuilder - - - - - - org.eclipse.jdt.core.javanature - org.eclipse.pde.PluginNature - - diff --git a/org.argeo.security.core/bnd.bnd b/org.argeo.security.core/bnd.bnd deleted file mode 100644 index e69de29bb..000000000 diff --git a/org.argeo.security.core/build.properties b/org.argeo.security.core/build.properties deleted file mode 100644 index af03ba43b..000000000 --- a/org.argeo.security.core/build.properties +++ /dev/null @@ -1,8 +0,0 @@ -source.. = src/,\ - ext/test/ -additional.bundles = org.junit,\ - org.slf4j.commons.logging,\ - org.slf4j.api,\ - org.slf4j.log4j12,\ - org.apache.log4j,\ - bitronix.tm diff --git a/org.argeo.security.core/ext/test/log4j.properties b/org.argeo.security.core/ext/test/log4j.properties deleted file mode 100644 index ef735668b..000000000 --- a/org.argeo.security.core/ext/test/log4j.properties +++ /dev/null @@ -1,28 +0,0 @@ -log4j.rootLogger=WARN, console - -## Levels -log4j.logger.org.argeo=TRACE - -log4j.logger.org.hibernate=WARN - -log4j.logger.org.springframework=WARN -#log4j.logger.org.springframework.web=DEBUG -#log4j.logger.org.springframework.jms=WARN -#log4j.logger.org.springframework.security=WARN - -log4j.logger.org.apache.activemq=WARN -log4j.logger.org.apache.activemq.transport=WARN -log4j.logger.org.apache.activemq.ActiveMQMessageConsumer=INFO -log4j.logger.org.apache.activemq.ActiveMQMessageProducer=INFO - -log4j.logger.org.apache.catalina=INFO -log4j.logger.org.apache.coyote=INFO -log4j.logger.org.apache.tomcat=INFO - -## Appenders -# console is set to be a ConsoleAppender. -log4j.appender.console=org.apache.log4j.ConsoleAppender - -# console uses PatternLayout. -log4j.appender.console.layout=org.apache.log4j.PatternLayout -log4j.appender.console.layout.ConversionPattern= %-5p %d{ISO8601} %m - %c%n diff --git a/org.argeo.security.core/ext/test/org/argeo/osgi/useradmin/BasicTestConstants.java b/org.argeo.security.core/ext/test/org/argeo/osgi/useradmin/BasicTestConstants.java deleted file mode 100644 index 98b8bc96d..000000000 --- a/org.argeo.security.core/ext/test/org/argeo/osgi/useradmin/BasicTestConstants.java +++ /dev/null @@ -1,9 +0,0 @@ -package org.argeo.osgi.useradmin; - -interface BasicTestConstants { - String BASE_DN = "dc=example,dc=com"; - String ROOT_USER_DN = "uid=root,ou=users," + BASE_DN; - String DEMO_USER_DN = "uid=demo,ou=users," + BASE_DN; - String ADMIN_GROUP_DN = "cn=admin,ou=groups," + BASE_DN; - String EDITORS_GROUP_DN = "cn=editors,ou=groups," + BASE_DN; -} diff --git a/org.argeo.security.core/ext/test/org/argeo/osgi/useradmin/LdifParserTest.java b/org.argeo.security.core/ext/test/org/argeo/osgi/useradmin/LdifParserTest.java deleted file mode 100644 index fecf5dd5a..000000000 --- a/org.argeo.security.core/ext/test/org/argeo/osgi/useradmin/LdifParserTest.java +++ /dev/null @@ -1,49 +0,0 @@ -package org.argeo.osgi.useradmin; - -import java.util.ArrayList; -import java.util.Base64; -import java.util.List; -import java.util.SortedMap; - -import javax.naming.NamingEnumeration; -import javax.naming.directory.Attribute; -import javax.naming.directory.Attributes; -import javax.naming.ldap.LdapName; - -import org.argeo.util.naming.LdifParser; - -import junit.framework.TestCase; - -public class LdifParserTest extends TestCase implements BasicTestConstants { - public void testBasicLdif() throws Exception { - LdifParser ldifParser = new LdifParser(); - SortedMap res = ldifParser.read(getClass() - .getResourceAsStream("basic.ldif")); - LdapName rootDn = new LdapName(ROOT_USER_DN); - Attributes rootAttributes = res.get(rootDn); - assertNotNull(rootAttributes); - assertEquals("Superuser", - rootAttributes.get(LdifName.description.name()).get()); - byte[] rawPwEntry = (byte[]) rootAttributes.get( - LdifName.userPassword.name()).get(); - assertEquals("{SHA}ieSV55Qc+eQOaYDRSha/AjzNTJE=", - new String(rawPwEntry)); - byte[] hashedPassword = DigestUtils.sha1("demo".getBytes()); - assertEquals("{SHA}" + Base64.getEncoder().encodeToString(hashedPassword), - new String(rawPwEntry)); - - LdapName adminDn = new LdapName(ADMIN_GROUP_DN); - Attributes adminAttributes = res.get(adminDn); - assertNotNull(adminAttributes); - Attribute memberAttribute = adminAttributes.get(LdifName.member.name()); - assertNotNull(memberAttribute); - NamingEnumeration members = memberAttribute.getAll(); - List users = new ArrayList(); - while (members.hasMore()) { - Object value = members.next(); - users.add(value.toString()); - } - assertEquals(1, users.size()); - assertEquals(rootDn, new LdapName(users.get(0))); - } -} diff --git a/org.argeo.security.core/ext/test/org/argeo/osgi/useradmin/LdifUserAdminTest.java b/org.argeo.security.core/ext/test/org/argeo/osgi/useradmin/LdifUserAdminTest.java deleted file mode 100644 index a8a7d226a..000000000 --- a/org.argeo.security.core/ext/test/org/argeo/osgi/useradmin/LdifUserAdminTest.java +++ /dev/null @@ -1,191 +0,0 @@ -package org.argeo.osgi.useradmin; - -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.File; -import java.io.InputStream; -import java.net.URI; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.Arrays; -import java.util.Base64; -import java.util.Dictionary; -import java.util.Hashtable; -import java.util.List; -import java.util.UUID; - -import javax.transaction.TransactionManager; - -import org.osgi.service.useradmin.Authorization; -import org.osgi.service.useradmin.Group; -import org.osgi.service.useradmin.Role; -import org.osgi.service.useradmin.User; - -import bitronix.tm.BitronixTransactionManager; -import bitronix.tm.TransactionManagerServices; -import bitronix.tm.resource.ehcache.EhCacheXAResourceProducer; -import junit.framework.TestCase; - -public class LdifUserAdminTest extends TestCase implements BasicTestConstants { - private BitronixTransactionManager tm; - private URI uri; - private AbstractUserDirectory userAdmin; - - public void testConcurrent() throws Exception { - } - - @SuppressWarnings("unchecked") - public void testEdition() throws Exception { - User demoUser = (User) userAdmin.getRole(DEMO_USER_DN); - assertNotNull(demoUser); - - tm.begin(); - String newName = "demo"; - demoUser.getProperties().put("cn", newName); - assertEquals(newName, demoUser.getProperties().get("cn")); - tm.commit(); - persistAndRestart(); - assertEquals(newName, demoUser.getProperties().get("cn")); - - tm.begin(); - userAdmin.removeRole(DEMO_USER_DN); - tm.commit(); - persistAndRestart(); - - // check data - Role[] search = userAdmin.getRoles("(objectclass=inetOrgPerson)"); - assertEquals(1, search.length); - Group editorGroup = (Group) userAdmin.getRole(EDITORS_GROUP_DN); - assertNotNull(editorGroup); - Role[] members = editorGroup.getMembers(); - assertEquals(1, members.length); - } - - public void testRetrieve() throws Exception { - // users - User rootUser = (User) userAdmin.getRole(ROOT_USER_DN); - assertNotNull(rootUser); - User demoUser = (User) userAdmin.getRole(DEMO_USER_DN); - assertNotNull(demoUser); - - // groups - Group adminGroup = (Group) userAdmin.getRole(ADMIN_GROUP_DN); - assertNotNull(adminGroup); - Role[] members = adminGroup.getMembers(); - assertEquals(1, members.length); - assertEquals(rootUser, members[0]); - - Group editorGroup = (Group) userAdmin.getRole(EDITORS_GROUP_DN); - assertNotNull(editorGroup); - members = editorGroup.getMembers(); - assertEquals(2, members.length); - assertEquals(adminGroup, members[0]); - assertEquals(demoUser, members[1]); - - Authorization rootAuth = userAdmin.getAuthorization(rootUser); - List rootRoles = Arrays.asList(rootAuth.getRoles()); - assertEquals(3, rootRoles.size()); - assertTrue(rootRoles.contains(ROOT_USER_DN)); - assertTrue(rootRoles.contains(ADMIN_GROUP_DN)); - assertTrue(rootRoles.contains(EDITORS_GROUP_DN)); - - // properties - assertEquals("root@localhost", rootUser.getProperties().get("mail")); - - // credentials - byte[] hashedPassword = ("{SHA}" + Base64.getEncoder().encodeToString(DigestUtils.sha1("demo".getBytes()))) - .getBytes(); - assertTrue(rootUser.hasCredential(LdifName.userPassword.name(), hashedPassword)); - assertTrue(demoUser.hasCredential(LdifName.userPassword.name(), hashedPassword)); - - // search - Role[] search = userAdmin.getRoles(null); - assertEquals(4, search.length); - search = userAdmin.getRoles("(objectClass=groupOfNames)"); - assertEquals(2, search.length); - search = userAdmin.getRoles("(objectclass=inetOrgPerson)"); - assertEquals(2, search.length); - search = userAdmin.getRoles("(&(objectclass=inetOrgPerson)(uid=demo))"); - assertEquals(1, search.length); - } - - public void testReadWriteRead() throws Exception { - if (userAdmin instanceof LdifUserAdmin) { - Dictionary props = userAdmin.getProperties(); - ByteArrayOutputStream out = new ByteArrayOutputStream(); - ((LdifUserAdmin) userAdmin).save(out); - byte[] arr = out.toByteArray(); - out.close(); - userAdmin.destroy(); - // String written = new String(arr); - // System.out.print(written); - try (ByteArrayInputStream in = new ByteArrayInputStream(arr)) { - userAdmin = new LdifUserAdmin(props); - ((LdifUserAdmin) userAdmin).load(in); - } - Role[] search = userAdmin.getRoles(null); - assertEquals(4, search.length); - } else { - // test not relevant for LDAP - } - } - - @Override - protected void setUp() throws Exception { - Path tempDir = Files.createTempDirectory(getClass().getName()); - String uriProp = System.getProperty("argeo.userdirectory.uri"); - if (uriProp != null) - uri = new URI(uriProp); - else { - tempDir.toFile().deleteOnExit(); - Path ldifPath = tempDir.resolve(BASE_DN + ".ldif"); - try (InputStream in = getClass().getResource("basic.ldif").openStream()) { - Files.copy(in, ldifPath); - } - uri = ldifPath.toUri(); - } - - bitronix.tm.Configuration tmConf = TransactionManagerServices.getConfiguration(); - tmConf.setServerId(UUID.randomUUID().toString()); - tmConf.setLogPart1Filename(new File(tempDir.toFile(), "btm1.tlog").getAbsolutePath()); - tmConf.setLogPart2Filename(new File(tempDir.toFile(), "btm2.tlog").getAbsolutePath()); - tm = TransactionManagerServices.getTransactionManager(); - - userAdmin = initUserAdmin(uri, tm); - } - - private AbstractUserDirectory initUserAdmin(URI uri, TransactionManager tm) { - Dictionary props = new Hashtable<>(); - props.put(UserAdminConf.uri.name(), uri.toString()); - props.put(UserAdminConf.baseDn.name(), BASE_DN); - props.put(UserAdminConf.userBase.name(), "ou=users"); - props.put(UserAdminConf.groupBase.name(), "ou=groups"); - AbstractUserDirectory userAdmin; - if (uri.getScheme().startsWith("ldap")) - userAdmin = new LdapUserAdmin(props); - else - userAdmin = new LdifUserAdmin(props); - userAdmin.init(); - // JTA - EhCacheXAResourceProducer.registerXAResource(UserDirectory.class.getName(), userAdmin.getXaResource()); - userAdmin.setTransactionManager(tm); - return userAdmin; - } - - private void persistAndRestart() { - EhCacheXAResourceProducer.unregisterXAResource(UserDirectory.class.getName(), userAdmin.getXaResource()); - if (userAdmin instanceof LdifUserAdmin) - ((LdifUserAdmin) userAdmin).save(); - userAdmin.destroy(); - userAdmin = initUserAdmin(uri, tm); - } - - @Override - protected void tearDown() throws Exception { - EhCacheXAResourceProducer.unregisterXAResource(UserDirectory.class.getName(), userAdmin.getXaResource()); - tm.shutdown(); - if (userAdmin != null) - userAdmin.destroy(); - } - -} diff --git a/org.argeo.security.core/ext/test/org/argeo/osgi/useradmin/basic.ldif b/org.argeo.security.core/ext/test/org/argeo/osgi/useradmin/basic.ldif deleted file mode 100644 index b7328b0b9..000000000 --- a/org.argeo.security.core/ext/test/org/argeo/osgi/useradmin/basic.ldif +++ /dev/null @@ -1,54 +0,0 @@ -dn: dc=example,dc=com -objectClass: domain -objectClass: extensibleObject -objectClass: top -dc: example - -dn: ou=groups,dc=example,dc=com -objectClass: organizationalUnit -objectClass: top -ou: groups - -dn: ou=users,dc=example,dc=com -objectClass: organizationalUnit -objectClass: top -ou: users - -dn: uid=demo,ou=users,dc=example,dc=com -objectClass: inetOrgPerson -objectClass: organizationalPerson -objectClass: person -objectClass: top -cn: Demo User -description: Demo user -givenName: Demo -mail: demo@localhost -sn: User -uid: demo -userPassword:: e1NIQX1pZVNWNTVRYytlUU9hWURSU2hhL0Fqek5USkU9 - -dn: uid=root,ou=users,dc=example,dc=com -objectClass: inetOrgPerson -objectClass: person -objectClass: organizationalPerson -objectClass: top -cn: Super User -description: Superuser -givenName: Super -mail: root@localhost -sn: User -uid: root -userPassword:: e1NIQX1pZVNWNTVRYytlUU9hWURSU2hhL0Fqek5USkU9 - -dn: cn=admin,ou=groups,dc=example,dc=com -objectClass: groupOfNames -objectClass: top -cn: admin -member: uid=root,ou=users,dc=example,dc=com - -dn: cn=editors,ou=groups,dc=example,dc=com -objectClass: groupOfNames -objectClass: top -cn: editors -member: cn=admin,ou=groups,dc=example,dc=com -member: uid=demo,ou=users,dc=example,dc=com diff --git a/org.argeo.security.core/pom.xml b/org.argeo.security.core/pom.xml deleted file mode 100644 index 56c2cfd5b..000000000 --- a/org.argeo.security.core/pom.xml +++ /dev/null @@ -1,24 +0,0 @@ - - - 4.0.0 - - org.argeo.commons - argeo-commons - 2.1.46-SNAPSHOT - .. - - org.argeo.security.core - Commons Security - - - org.argeo.commons - org.argeo.util - 2.1.46-SNAPSHOT - - - org.argeo.commons - org.argeo.server.jcr - 2.1.46-SNAPSHOT - - - \ No newline at end of file diff --git a/org.argeo.security.core/src/org/argeo/osgi/useradmin/AbstractUserDirectory.java b/org.argeo.security.core/src/org/argeo/osgi/useradmin/AbstractUserDirectory.java deleted file mode 100644 index 3f5bf850d..000000000 --- a/org.argeo.security.core/src/org/argeo/osgi/useradmin/AbstractUserDirectory.java +++ /dev/null @@ -1,426 +0,0 @@ -package org.argeo.osgi.useradmin; - -import static org.argeo.osgi.useradmin.LdifName.inetOrgPerson; -import static org.argeo.osgi.useradmin.LdifName.objectClass; -import static org.argeo.osgi.useradmin.LdifName.organizationalPerson; -import static org.argeo.osgi.useradmin.LdifName.person; -import static org.argeo.osgi.useradmin.LdifName.top; - -import java.io.File; -import java.net.URI; -import java.net.URISyntaxException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Dictionary; -import java.util.Enumeration; -import java.util.Hashtable; -import java.util.Iterator; -import java.util.List; - -import javax.naming.InvalidNameException; -import javax.naming.directory.Attributes; -import javax.naming.directory.BasicAttribute; -import javax.naming.directory.BasicAttributes; -import javax.naming.ldap.LdapName; -import javax.naming.ldap.Rdn; -import javax.transaction.SystemException; -import javax.transaction.Transaction; -import javax.transaction.TransactionManager; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.osgi.framework.Filter; -import org.osgi.framework.FrameworkUtil; -import org.osgi.framework.InvalidSyntaxException; -import org.osgi.service.useradmin.Authorization; -import org.osgi.service.useradmin.Role; -import org.osgi.service.useradmin.User; -import org.osgi.service.useradmin.UserAdmin; - -/** Base class for a {@link UserDirectory}. */ -public abstract class AbstractUserDirectory implements UserAdmin, UserDirectory { - private final static Log log = LogFactory.getLog(AbstractUserDirectory.class); - - private final Hashtable properties; - private final LdapName baseDn; - private final String userObjectClass, userBase, groupObjectClass, groupBase; - - private final boolean readOnly; - private final URI uri; - - private UserAdmin externalRoles; - private List indexedUserProperties = Arrays - .asList(new String[] { LdifName.uid.name(), LdifName.mail.name(), LdifName.cn.name() }); - - private String memberAttributeId = "member"; - private List credentialAttributeIds = Arrays.asList(new String[] { LdifName.userPassword.name() }); - - // JTA - private TransactionManager transactionManager; - private WcXaResource xaResource = new WcXaResource(this); - - public AbstractUserDirectory(Dictionary props) { - properties = new Hashtable(); - for (Enumeration keys = props.keys(); keys.hasMoreElements();) { - String key = keys.nextElement(); - properties.put(key, props.get(key)); - } - - String uriStr = UserAdminConf.uri.getValue(properties); - if (uriStr == null) - uri = null; - else - try { - uri = new URI(uriStr); - } catch (URISyntaxException e) { - throw new UserDirectoryException("Badly formatted URI " + uriStr, e); - } - - try { - baseDn = new LdapName(UserAdminConf.baseDn.getValue(properties)); - } catch (InvalidNameException e) { - throw new UserDirectoryException("Badly formated base DN " + UserAdminConf.baseDn.getValue(properties), e); - } - String readOnlyStr = UserAdminConf.readOnly.getValue(properties); - if (readOnlyStr == null) { - readOnly = readOnlyDefault(uri); - properties.put(UserAdminConf.readOnly.name(), Boolean.toString(readOnly)); - } else - readOnly = new Boolean(readOnlyStr); - - userObjectClass = UserAdminConf.userObjectClass.getValue(properties); - userBase = UserAdminConf.userBase.getValue(properties); - groupObjectClass = UserAdminConf.groupObjectClass.getValue(properties); - groupBase = UserAdminConf.groupBase.getValue(properties); - } - - /** Returns the groups this user is a direct member of. */ - protected abstract List getDirectGroups(LdapName dn); - - protected abstract Boolean daoHasRole(LdapName dn); - - protected abstract DirectoryUser daoGetRole(LdapName key); - - protected abstract List doGetRoles(Filter f); - - public void init() { - - } - - public void destroy() { - - } - - protected boolean isEditing() { - return xaResource.wc() != null; - } - - protected UserDirectoryWorkingCopy getWorkingCopy() { - UserDirectoryWorkingCopy wc = xaResource.wc(); - if (wc == null) - return null; - return wc; - } - - protected void checkEdit() { - Transaction transaction; - try { - transaction = transactionManager.getTransaction(); - } catch (SystemException e) { - throw new UserDirectoryException("Cannot get transaction", e); - } - if (transaction == null) - throw new UserDirectoryException("A transaction needs to be active in order to edit"); - if (xaResource.wc() == null) { - try { - transaction.enlistResource(xaResource); - } catch (Exception e) { - throw new UserDirectoryException("Cannot enlist " + xaResource, e); - } - } else { - } - } - - protected List getAllRoles(DirectoryUser user) { - List allRoles = new ArrayList(); - if (user != null) { - collectRoles(user, allRoles); - allRoles.add(user); - } else - collectAnonymousRoles(allRoles); - return allRoles; - } - - private void collectRoles(DirectoryUser user, List allRoles) { - for (LdapName groupDn : getDirectGroups(user.getDn())) { - // TODO check for loops - DirectoryUser group = doGetRole(groupDn); - allRoles.add(group); - collectRoles(group, allRoles); - } - } - - private void collectAnonymousRoles(List allRoles) { - // TODO gather anonymous roles - } - - // USER ADMIN - @Override - public Role getRole(String name) { - return doGetRole(toDn(name)); - } - - protected DirectoryUser doGetRole(LdapName dn) { - UserDirectoryWorkingCopy wc = getWorkingCopy(); - DirectoryUser user = daoGetRole(dn); - if (wc != null) { - if (user == null && wc.getNewUsers().containsKey(dn)) - user = wc.getNewUsers().get(dn); - else if (wc.getDeletedUsers().containsKey(dn)) - user = null; - } - return user; - } - - @SuppressWarnings("unchecked") - @Override - public Role[] getRoles(String filter) throws InvalidSyntaxException { - UserDirectoryWorkingCopy wc = getWorkingCopy(); - Filter f = filter != null ? FrameworkUtil.createFilter(filter) : null; - List res = doGetRoles(f); - if (wc != null) { - for (Iterator it = res.iterator(); it.hasNext();) { - DirectoryUser user = it.next(); - LdapName dn = user.getDn(); - if (wc.getDeletedUsers().containsKey(dn)) - it.remove(); - } - for (DirectoryUser user : wc.getNewUsers().values()) { - if (f == null || f.match(user.getProperties())) - res.add(user); - } - // no need to check modified users, - // since doGetRoles was already based on the modified attributes - } - return res.toArray(new Role[res.size()]); - } - - @Override - public User getUser(String key, String value) { - // TODO check value null or empty - List collectedUsers = new ArrayList(getIndexedUserProperties().size()); - if (key != null) { - doGetUser(key, value, collectedUsers); - } else { - // try dn - DirectoryUser user = null; - try { - user = (DirectoryUser) getRole(value); - if (user != null) - collectedUsers.add(user); - } catch (Exception e) { - // silent - } - // try all indexes - for (String attr : getIndexedUserProperties()) - doGetUser(attr, value, collectedUsers); - } - if (collectedUsers.size() == 1) - return collectedUsers.get(0); - else if (collectedUsers.size() > 1) - log.warn(collectedUsers.size() + " users for " + (key != null ? key + "=" : "") + value); - return null; - } - - protected void doGetUser(String key, String value, List collectedUsers) { - try { - Filter f = FrameworkUtil.createFilter("(" + key + "=" + value + ")"); - List users = doGetRoles(f); - collectedUsers.addAll(users); - } catch (InvalidSyntaxException e) { - throw new UserDirectoryException("Cannot get user with " + key + "=" + value, e); - } - } - - @Override - public Authorization getAuthorization(User user) { - return new LdifAuthorization((DirectoryUser) user, getAllRoles((DirectoryUser) user)); - } - - @Override - public Role createRole(String name, int type) { - checkEdit(); - UserDirectoryWorkingCopy wc = getWorkingCopy(); - LdapName dn = toDn(name); - if ((daoHasRole(dn) && !wc.getDeletedUsers().containsKey(dn)) || wc.getNewUsers().containsKey(dn)) - throw new UserDirectoryException("Already a role " + name); - BasicAttributes attrs = new BasicAttributes(true); - // attrs.put(LdifName.dn.name(), dn.toString()); - Rdn nameRdn = dn.getRdn(dn.size() - 1); - // TODO deal with multiple attr RDN - attrs.put(nameRdn.getType(), nameRdn.getValue()); - if (wc.getDeletedUsers().containsKey(dn)) { - wc.getDeletedUsers().remove(dn); - wc.getModifiedUsers().put(dn, attrs); - } else { - wc.getModifiedUsers().put(dn, attrs); - DirectoryUser newRole = newRole(dn, type, attrs); - wc.getNewUsers().put(dn, newRole); - } - return getRole(name); - } - - protected DirectoryUser newRole(LdapName dn, int type, Attributes attrs) { - LdifUser newRole; - BasicAttribute objClass = new BasicAttribute(objectClass.name()); - if (type == Role.USER) { - String userObjClass = newUserObjectClass(dn); - objClass.add(userObjClass); - if (inetOrgPerson.name().equals(userObjClass)) { - objClass.add(organizationalPerson.name()); - objClass.add(person.name()); - } else if (organizationalPerson.name().equals(userObjClass)) { - objClass.add(person.name()); - } - objClass.add(top.name()); - attrs.put(objClass); - newRole = new LdifUser(this, dn, attrs); - } else if (type == Role.GROUP) { - String groupObjClass = getGroupObjectClass(); - objClass.add(groupObjClass); - // objClass.add(LdifName.extensibleObject.name()); - objClass.add(top.name()); - attrs.put(objClass); - newRole = new LdifGroup(this, dn, attrs); - } else - throw new UserDirectoryException("Unsupported type " + type); - return newRole; - } - - @Override - public boolean removeRole(String name) { - checkEdit(); - UserDirectoryWorkingCopy wc = getWorkingCopy(); - LdapName dn = toDn(name); - boolean actuallyDeleted; - if (daoHasRole(dn) || wc.getNewUsers().containsKey(dn)) { - DirectoryUser user = (DirectoryUser) getRole(name); - wc.getDeletedUsers().put(dn, user); - actuallyDeleted = true; - } else {// just removing from groups (e.g. system roles) - actuallyDeleted = false; - } - for (LdapName groupDn : getDirectGroups(dn)) { - DirectoryUser group = doGetRole(groupDn); - group.getAttributes().get(getMemberAttributeId()).remove(dn.toString()); - } - return actuallyDeleted; - } - - // TRANSACTION - protected void prepare(UserDirectoryWorkingCopy wc) { - - } - - protected void commit(UserDirectoryWorkingCopy wc) { - - } - - protected void rollback(UserDirectoryWorkingCopy wc) { - - } - - // UTILITIES - protected LdapName toDn(String name) { - try { - return new LdapName(name); - } catch (InvalidNameException e) { - throw new UserDirectoryException("Badly formatted name", e); - } - } - - // GETTERS - protected String getMemberAttributeId() { - return memberAttributeId; - } - - protected List getCredentialAttributeIds() { - return credentialAttributeIds; - } - - protected URI getUri() { - return uri; - } - - protected List getIndexedUserProperties() { - return indexedUserProperties; - } - - protected void setIndexedUserProperties(List indexedUserProperties) { - this.indexedUserProperties = indexedUserProperties; - } - - private static boolean readOnlyDefault(URI uri) { - if (uri == null) - return true; - if (uri.getScheme().equals("file")) { - File file = new File(uri); - if (file.exists()) - return !file.canWrite(); - else - return !file.getParentFile().canWrite(); - } - return true; - } - - public boolean isReadOnly() { - return readOnly; - } - - protected UserAdmin getExternalRoles() { - return externalRoles; - } - - public LdapName getBaseDn() { - // always clone so that the property is not modified by reference - return (LdapName) baseDn.clone(); - } - - /** dn can be null, in that case a default should be returned. */ - public String getUserObjectClass() { - return userObjectClass; - } - - public String getUserBase() { - return userBase; - } - - protected String newUserObjectClass(LdapName dn) { - return getUserObjectClass(); - } - - public String getGroupObjectClass() { - return groupObjectClass; - } - - public String getGroupBase() { - return groupBase; - } - - public Dictionary getProperties() { - return properties; - } - - public void setExternalRoles(UserAdmin externalRoles) { - this.externalRoles = externalRoles; - } - - public void setTransactionManager(TransactionManager transactionManager) { - this.transactionManager = transactionManager; - } - - public WcXaResource getXaResource() { - return xaResource; - } - -} diff --git a/org.argeo.security.core/src/org/argeo/osgi/useradmin/AggregatingAuthorization.java b/org.argeo.security.core/src/org/argeo/osgi/useradmin/AggregatingAuthorization.java deleted file mode 100644 index f270b8d89..000000000 --- a/org.argeo.security.core/src/org/argeo/osgi/useradmin/AggregatingAuthorization.java +++ /dev/null @@ -1,73 +0,0 @@ -package org.argeo.osgi.useradmin; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.List; - -import javax.security.auth.x500.X500Principal; - -import org.osgi.service.useradmin.Authorization; - -class AggregatingAuthorization implements Authorization { - private final String name; - private final String displayName; - private final List systemRoles; - private final List roles; - - public AggregatingAuthorization(String name, String displayName, - Collection systemRoles, String[] roles) { - this.name = new X500Principal(name).getName(); - this.displayName = displayName; - this.systemRoles = Collections.unmodifiableList(new ArrayList( - systemRoles)); - this.roles = Collections.unmodifiableList(Arrays.asList(roles)); - } - - @Override - public String getName() { - return name; - } - - @Override - public boolean hasRole(String name) { - if (systemRoles.contains(name)) - return true; - if (roles.contains(name)) - return true; - return false; - } - - @Override - public String[] getRoles() { - int size = systemRoles.size() + roles.size(); - List res = new ArrayList(size); - res.addAll(systemRoles); - res.addAll(roles); - return res.toArray(new String[size]); - } - - @Override - public int hashCode() { - if (name == null) - return super.hashCode(); - return name.hashCode(); - } - - @Override - public boolean equals(Object obj) { - if (!(obj instanceof Authorization)) - return false; - Authorization that = (Authorization) obj; - if (name == null) - return that.getName() == null; - return name.equals(that.getName()); - } - - @Override - public String toString() { - return displayName; - } - -} diff --git a/org.argeo.security.core/src/org/argeo/osgi/useradmin/AggregatingUserAdmin.java b/org.argeo.security.core/src/org/argeo/osgi/useradmin/AggregatingUserAdmin.java deleted file mode 100644 index 860c5efc9..000000000 --- a/org.argeo.security.core/src/org/argeo/osgi/useradmin/AggregatingUserAdmin.java +++ /dev/null @@ -1,189 +0,0 @@ -package org.argeo.osgi.useradmin; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Dictionary; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Hashtable; -import java.util.List; -import java.util.Map; -import java.util.Set; - -import javax.naming.InvalidNameException; -import javax.naming.ldap.LdapName; - -import org.osgi.framework.InvalidSyntaxException; -import org.osgi.service.useradmin.Authorization; -import org.osgi.service.useradmin.Role; -import org.osgi.service.useradmin.User; -import org.osgi.service.useradmin.UserAdmin; - -/** - * Aggregates multiple {@link UserDirectory} and integrates them with system - * roles. - */ -public class AggregatingUserAdmin implements UserAdmin { - private final LdapName systemRolesBaseDn; - - // DAOs - private AbstractUserDirectory systemRoles = null; - private Map businessRoles = new HashMap(); - - public AggregatingUserAdmin(String systemRolesBaseDn) { - try { - this.systemRolesBaseDn = new LdapName(systemRolesBaseDn); - } catch (InvalidNameException e) { - throw new UserDirectoryException("Cannot initialize " + AggregatingUserAdmin.class, e); - } - } - - @Override - public Role createRole(String name, int type) { - return findUserAdmin(name).createRole(name, type); - } - - @Override - public boolean removeRole(String name) { - boolean actuallyDeleted = findUserAdmin(name).removeRole(name); - systemRoles.removeRole(name); - return actuallyDeleted; - } - - @Override - public Role getRole(String name) { - return findUserAdmin(name).getRole(name); - } - - @Override - public Role[] getRoles(String filter) throws InvalidSyntaxException { - List res = new ArrayList(); - for (UserAdmin userAdmin : businessRoles.values()) { - res.addAll(Arrays.asList(userAdmin.getRoles(filter))); - } - res.addAll(Arrays.asList(systemRoles.getRoles(filter))); - return res.toArray(new Role[res.size()]); - } - - @Override - public User getUser(String key, String value) { - List res = new ArrayList(); - for (UserAdmin userAdmin : businessRoles.values()) { - User u = userAdmin.getUser(key, value); - if (u != null) - res.add(u); - } - // Note: node roles cannot contain users, so it is not searched - return res.size() == 1 ? res.get(0) : null; - } - - @Override - public Authorization getAuthorization(User user) { - if (user == null) {// anonymous - return systemRoles.getAuthorization(null); - } - UserAdmin userAdmin = findUserAdmin(user.getName()); - Authorization rawAuthorization = userAdmin.getAuthorization(user); - // gather system roles - Set sysRoles = new HashSet(); - for (String role : rawAuthorization.getRoles()) { - Authorization auth = systemRoles.getAuthorization((User) userAdmin.getRole(role)); - sysRoles.addAll(Arrays.asList(auth.getRoles())); - } - Authorization authorization = new AggregatingAuthorization(rawAuthorization.getName(), - rawAuthorization.toString(), sysRoles, rawAuthorization.getRoles()); - return authorization; - } - - // - // USER ADMIN AGGREGATOR - // - protected void addUserDirectory(AbstractUserDirectory userDirectory) { - LdapName baseDn = userDirectory.getBaseDn(); - if (isSystemRolesBaseDn(baseDn)) { - this.systemRoles = userDirectory; - systemRoles.setExternalRoles(this); - } else { - if (businessRoles.containsKey(baseDn)) - throw new UserDirectoryException("There is already a user admin for " + baseDn); - businessRoles.put(baseDn, userDirectory); - } - userDirectory.init(); - postAdd(userDirectory); - } - - /** Called after a new user directory has been added */ - protected void postAdd(AbstractUserDirectory userDirectory) { - } - - private UserAdmin findUserAdmin(String name) { - try { - return findUserAdmin(new LdapName(name)); - } catch (InvalidNameException e) { - throw new UserDirectoryException("Badly formatted name " + name, e); - } - } - - private UserAdmin findUserAdmin(LdapName name) { - if (name.startsWith(systemRolesBaseDn)) - return systemRoles; - List res = new ArrayList(1); - for (LdapName baseDn : businessRoles.keySet()) { - if (name.startsWith(baseDn)) - res.add(businessRoles.get(baseDn)); - } - if (res.size() == 0) - throw new UserDirectoryException("Cannot find user admin for " + name); - if (res.size() > 1) - throw new UserDirectoryException("Multiple user admin found for " + name); - return res.get(0); - } - - protected boolean isSystemRolesBaseDn(LdapName baseDn) { - return baseDn.equals(systemRolesBaseDn); - } - - protected Dictionary currentState() { - Dictionary res = new Hashtable(); - // res.put(NodeConstants.CN, NodeConstants.DEFAULT); - for (LdapName name : businessRoles.keySet()) { - AbstractUserDirectory userDirectory = businessRoles.get(name); - String uri = UserAdminConf.propertiesAsUri(userDirectory.getProperties()).toString(); - res.put(uri, ""); - } - return res; - } - - public void destroy() { - for (LdapName name : businessRoles.keySet()) { - AbstractUserDirectory userDirectory = businessRoles.get(name); - destroy(userDirectory); - } - businessRoles.clear(); - businessRoles = null; - destroy(systemRoles); - systemRoles = null; - } - - private void destroy(AbstractUserDirectory userDirectory) { - preDestroy(userDirectory); - userDirectory.destroy(); - } - - protected void removeUserDirectory(LdapName baseDn) { - if (isSystemRolesBaseDn(baseDn)) - throw new UserDirectoryException("System roles cannot be removed "); - if (!businessRoles.containsKey(baseDn)) - throw new UserDirectoryException("No user directory registered for " + baseDn); - AbstractUserDirectory userDirectory = businessRoles.remove(baseDn); - destroy(userDirectory); - } - - /** - * Called before each user directory is destroyed, so that additional - * actions can be performed. - */ - protected void preDestroy(UserDirectory userDirectory) { - } - -} diff --git a/org.argeo.security.core/src/org/argeo/osgi/useradmin/DigestUtils.java b/org.argeo.security.core/src/org/argeo/osgi/useradmin/DigestUtils.java deleted file mode 100644 index d8f8ce9d5..000000000 --- a/org.argeo.security.core/src/org/argeo/osgi/useradmin/DigestUtils.java +++ /dev/null @@ -1,20 +0,0 @@ -package org.argeo.osgi.useradmin; - -import java.security.MessageDigest; - -class DigestUtils { - static byte[] sha1(byte[] bytes) { - try { - MessageDigest digest = MessageDigest.getInstance("SHA1"); - digest.update(bytes); - byte[] checksum = digest.digest(); - return checksum; - } catch (Exception e) { - throw new UserDirectoryException("Cannot SHA1 digest", e); - } - } - - private DigestUtils() { - } - -} diff --git a/org.argeo.security.core/src/org/argeo/osgi/useradmin/DirectoryGroup.java b/org.argeo.security.core/src/org/argeo/osgi/useradmin/DirectoryGroup.java deleted file mode 100644 index 7f8046313..000000000 --- a/org.argeo.security.core/src/org/argeo/osgi/useradmin/DirectoryGroup.java +++ /dev/null @@ -1,12 +0,0 @@ -package org.argeo.osgi.useradmin; - -import java.util.List; - -import javax.naming.ldap.LdapName; - -import org.osgi.service.useradmin.Group; - -/** A group in a user directroy. */ -interface DirectoryGroup extends Group, DirectoryUser { - List getMemberNames(); -} diff --git a/org.argeo.security.core/src/org/argeo/osgi/useradmin/DirectoryUser.java b/org.argeo.security.core/src/org/argeo/osgi/useradmin/DirectoryUser.java deleted file mode 100644 index 146b80578..000000000 --- a/org.argeo.security.core/src/org/argeo/osgi/useradmin/DirectoryUser.java +++ /dev/null @@ -1,15 +0,0 @@ -package org.argeo.osgi.useradmin; - -import javax.naming.directory.Attributes; -import javax.naming.ldap.LdapName; - -import org.osgi.service.useradmin.User; - -/** A user in a user directory. */ -interface DirectoryUser extends User { - LdapName getDn(); - - Attributes getAttributes(); - - void publishAttributes(Attributes modifiedAttributes); -} diff --git a/org.argeo.security.core/src/org/argeo/osgi/useradmin/LdapUserAdmin.java b/org.argeo.security.core/src/org/argeo/osgi/useradmin/LdapUserAdmin.java deleted file mode 100644 index 7a617dfd6..000000000 --- a/org.argeo.security.core/src/org/argeo/osgi/useradmin/LdapUserAdmin.java +++ /dev/null @@ -1,227 +0,0 @@ -package org.argeo.osgi.useradmin; - -import static org.argeo.osgi.useradmin.LdifName.objectClass; - -import java.util.ArrayList; -import java.util.Dictionary; -import java.util.Hashtable; -import java.util.List; - -import javax.naming.Binding; -import javax.naming.Context; -import javax.naming.InvalidNameException; -import javax.naming.NameNotFoundException; -import javax.naming.NamingEnumeration; -import javax.naming.NamingException; -import javax.naming.directory.Attribute; -import javax.naming.directory.Attributes; -import javax.naming.directory.DirContext; -import javax.naming.directory.SearchControls; -import javax.naming.directory.SearchResult; -import javax.naming.ldap.InitialLdapContext; -import javax.naming.ldap.LdapName; -import javax.transaction.TransactionManager; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.osgi.framework.Filter; - -/** - * A user admin based on a LDAP server. Requires a {@link TransactionManager} - * and an open transaction for write access. - */ -public class LdapUserAdmin extends AbstractUserDirectory { - private final static Log log = LogFactory.getLog(LdapUserAdmin.class); - - private InitialLdapContext initialLdapContext = null; - - public LdapUserAdmin(Dictionary properties) { - super(properties); - try { - Hashtable connEnv = new Hashtable(); - connEnv.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory"); - connEnv.put(Context.PROVIDER_URL, getUri().toString()); - connEnv.put("java.naming.ldap.attributes.binary", LdifName.userPassword.name()); - - initialLdapContext = new InitialLdapContext(connEnv, null); - // StartTlsResponse tls = (StartTlsResponse) ctx - // .extendedOperation(new StartTlsRequest()); - // tls.negotiate(); - initialLdapContext.addToEnvironment(Context.SECURITY_AUTHENTICATION, "simple"); - Object principal = properties.get(Context.SECURITY_PRINCIPAL); - if (principal != null) { - initialLdapContext.addToEnvironment(Context.SECURITY_PRINCIPAL, principal.toString()); - Object creds = properties.get(Context.SECURITY_CREDENTIALS); - if (creds != null) { - initialLdapContext.addToEnvironment(Context.SECURITY_CREDENTIALS, creds.toString()); - - } - } - // initialLdapContext.addToEnvironment(Context.SECURITY_PRINCIPAL, - // "uid=admin,ou=system"); - // initialLdapContext.addToEnvironment(Context.SECURITY_CREDENTIALS, - // "secret"); - } catch (Exception e) { - throw new UserDirectoryException("Cannot connect to LDAP", e); - } - } - - public void destroy() { - try { - // tls.close(); - initialLdapContext.close(); - } catch (NamingException e) { - log.error("Cannot destroy LDAP user admin", e); - } - } - - protected InitialLdapContext getLdapContext() { - return initialLdapContext; - } - - @Override - protected Boolean daoHasRole(LdapName dn) { - return daoGetRole(dn) != null; - } - - @Override - protected DirectoryUser daoGetRole(LdapName name) { - try { - Attributes attrs = getLdapContext().getAttributes(name); - if (attrs.size() == 0) - return null; - LdifUser res; - if (attrs.get(objectClass.name()).contains(getGroupObjectClass())) - res = new LdifGroup(this, name, attrs); - else if (attrs.get(objectClass.name()).contains(getUserObjectClass())) - res = new LdifUser(this, name, attrs); - else - throw new UserDirectoryException("Unsupported LDAP type for " + name); - return res; - } catch (NamingException e) { - return null; - } - } - - @Override - protected List doGetRoles(Filter f) { - try { - String searchFilter = f != null ? f.toString() - : "(|(" + objectClass + "=" + getUserObjectClass() + ")(" + objectClass + "=" - + getGroupObjectClass() + "))"; - SearchControls searchControls = new SearchControls(); - searchControls.setSearchScope(SearchControls.SUBTREE_SCOPE); - - LdapName searchBase = getBaseDn(); - NamingEnumeration results = getLdapContext().search(searchBase, searchFilter, searchControls); - - ArrayList res = new ArrayList(); - results: while (results.hasMoreElements()) { - SearchResult searchResult = results.next(); - Attributes attrs = searchResult.getAttributes(); - Attribute objectClassAttr = attrs.get(objectClass.name()); - LdapName dn = toDn(searchBase, searchResult); - LdifUser role; - if (objectClassAttr.contains(getGroupObjectClass())) - role = new LdifGroup(this, dn, attrs); - else if (objectClassAttr.contains(getUserObjectClass())) - role = new LdifUser(this, dn, attrs); - else { - log.warn("Unsupported LDAP type for " + searchResult.getName()); - continue results; - } - res.add(role); - } - return res; - } catch (Exception e) { - throw new UserDirectoryException("Cannot get roles for filter " + f, e); - } - } - - private LdapName toDn(LdapName baseDn, Binding binding) throws InvalidNameException { - return new LdapName(binding.isRelative() ? binding.getName() + "," + baseDn : binding.getName()); - } - - @Override - protected List getDirectGroups(LdapName dn) { - List directGroups = new ArrayList(); - try { - String searchFilter = "(&(" + objectClass + "=" + getGroupObjectClass() + ")(" + getMemberAttributeId() - + "=" + dn + "))"; - - SearchControls searchControls = new SearchControls(); - searchControls.setSearchScope(SearchControls.SUBTREE_SCOPE); - - LdapName searchBase = getBaseDn(); - NamingEnumeration results = getLdapContext().search(searchBase, searchFilter, searchControls); - - while (results.hasMoreElements()) { - SearchResult searchResult = (SearchResult) results.nextElement(); - directGroups.add(toDn(searchBase, searchResult)); - } - return directGroups; - } catch (Exception e) { - throw new UserDirectoryException("Cannot populate direct members of " + dn, e); - } - } - - @Override - protected void prepare(UserDirectoryWorkingCopy wc) { - try { - getLdapContext().reconnect(getLdapContext().getConnectControls()); - // delete - for (LdapName dn : wc.getDeletedUsers().keySet()) { - if (!entryExists(dn)) - throw new UserDirectoryException("User to delete no found " + dn); - } - // add - for (LdapName dn : wc.getNewUsers().keySet()) { - if (entryExists(dn)) - throw new UserDirectoryException("User to create found " + dn); - } - // modify - for (LdapName dn : wc.getModifiedUsers().keySet()) { - if (!wc.getNewUsers().containsKey(dn) && !entryExists(dn)) - throw new UserDirectoryException("User to modify not found " + dn); - } - } catch (NamingException e) { - throw new UserDirectoryException("Cannot prepare LDAP", e); - } - } - - private boolean entryExists(LdapName dn) throws NamingException { - try { - return getLdapContext().getAttributes(dn).size() != 0; - } catch (NameNotFoundException e) { - return false; - } - } - - @Override - protected void commit(UserDirectoryWorkingCopy wc) { - try { - // delete - for (LdapName dn : wc.getDeletedUsers().keySet()) { - getLdapContext().destroySubcontext(dn); - } - // add - for (LdapName dn : wc.getNewUsers().keySet()) { - DirectoryUser user = wc.getNewUsers().get(dn); - getLdapContext().createSubcontext(dn, user.getAttributes()); - } - // modify - for (LdapName dn : wc.getModifiedUsers().keySet()) { - Attributes modifiedAttrs = wc.getModifiedUsers().get(dn); - getLdapContext().modifyAttributes(dn, DirContext.REPLACE_ATTRIBUTE, modifiedAttrs); - } - } catch (NamingException e) { - throw new UserDirectoryException("Cannot commit LDAP", e); - } - } - - @Override - protected void rollback(UserDirectoryWorkingCopy wc) { - // prepare not impacting - } - -} diff --git a/org.argeo.security.core/src/org/argeo/osgi/useradmin/LdifAuthorization.java b/org.argeo.security.core/src/org/argeo/osgi/useradmin/LdifAuthorization.java deleted file mode 100644 index e06c42e1f..000000000 --- a/org.argeo.security.core/src/org/argeo/osgi/useradmin/LdifAuthorization.java +++ /dev/null @@ -1,82 +0,0 @@ -package org.argeo.osgi.useradmin; - -import java.util.Arrays; -import java.util.Collections; -import java.util.Dictionary; -import java.util.List; - -import org.osgi.service.useradmin.Authorization; -import org.osgi.service.useradmin.Role; -import org.osgi.service.useradmin.User; - -/** Basic authorization. */ -class LdifAuthorization implements Authorization { - private final String name; - private final String displayName; - private final List allRoles; - - @SuppressWarnings("unchecked") - public LdifAuthorization(User user, List allRoles) { - if (user == null) { - this.name = null; - this.displayName = "anonymous"; - } else { - this.name = user.getName(); - Dictionary props = user.getProperties(); - Object displayName = props.get(LdifName.displayName); - if (displayName == null) - displayName = props.get(LdifName.cn); - if (displayName == null) - displayName = props.get(LdifName.uid); - if (displayName == null) - displayName = user.getName(); - if (displayName == null) - throw new UserDirectoryException("Cannot set display name for " - + user); - this.displayName = displayName.toString(); - } - // roles - String[] roles = new String[allRoles.size()]; - for (int i = 0; i < allRoles.size(); i++) { - roles[i] = allRoles.get(i).getName(); - } - this.allRoles = Collections.unmodifiableList(Arrays.asList(roles)); - } - - @Override - public String getName() { - return name; - } - - @Override - public boolean hasRole(String name) { - return allRoles.contains(name); - } - - @Override - public String[] getRoles() { - return allRoles.toArray(new String[allRoles.size()]); - } - - @Override - public int hashCode() { - if (name == null) - return super.hashCode(); - return name.hashCode(); - } - - @Override - public boolean equals(Object obj) { - if (!(obj instanceof Authorization)) - return false; - Authorization that = (Authorization) obj; - if (name == null) - return that.getName() == null; - return name.equals(that.getName()); - } - - @Override - public String toString() { - return displayName; - } -} diff --git a/org.argeo.security.core/src/org/argeo/osgi/useradmin/LdifGroup.java b/org.argeo.security.core/src/org/argeo/osgi/useradmin/LdifGroup.java deleted file mode 100644 index bd129119b..000000000 --- a/org.argeo.security.core/src/org/argeo/osgi/useradmin/LdifGroup.java +++ /dev/null @@ -1,106 +0,0 @@ -package org.argeo.osgi.useradmin; - -import java.util.ArrayList; -import java.util.List; - -import javax.naming.NamingEnumeration; -import javax.naming.directory.Attribute; -import javax.naming.directory.Attributes; -import javax.naming.ldap.LdapName; - -import org.osgi.service.useradmin.Role; - -/** Directory group implementation */ -class LdifGroup extends LdifUser implements DirectoryGroup { - private final String memberAttributeId; - - LdifGroup(AbstractUserDirectory userAdmin, LdapName dn, - Attributes attributes) { - super(userAdmin, dn, attributes); - memberAttributeId = userAdmin.getMemberAttributeId(); - } - - @Override - public boolean addMember(Role role) { - getUserAdmin().checkEdit(); - if (!isEditing()) - startEditing(); - - Attribute member = getAttributes().get(memberAttributeId); - if (member != null) { - if (member.contains(role.getName())) - return false; - else - member.add(role.getName()); - } else - getAttributes().put(memberAttributeId, role.getName()); - return true; - } - - @Override - public boolean addRequiredMember(Role role) { - throw new UnsupportedOperationException(); - } - - @Override - public boolean removeMember(Role role) { - getUserAdmin().checkEdit(); - if (!isEditing()) - startEditing(); - - Attribute member = getAttributes().get(memberAttributeId); - if (member != null) { - if (!member.contains(role.getName())) - return false; - member.remove(role.getName()); - return true; - } else - return false; - } - - @Override - public Role[] getMembers() { - List directMembers = new ArrayList(); - for (LdapName ldapName : getMemberNames()) { - Role role = getUserAdmin().getRole(ldapName.toString()); - if (role == null) { - if (getUserAdmin().getExternalRoles() != null) - role = getUserAdmin().getExternalRoles().getRole( - ldapName.toString()); - } - if (role == null) - throw new UserDirectoryException("No role found for " - + ldapName); - directMembers.add(role); - } - return directMembers.toArray(new Role[directMembers.size()]); - } - - @Override - public List getMemberNames() { - Attribute memberAttribute = getAttributes().get(memberAttributeId); - if (memberAttribute == null) - return new ArrayList(); - try { - List roles = new ArrayList(); - NamingEnumeration values = memberAttribute.getAll(); - while (values.hasMore()) { - LdapName dn = new LdapName(values.next().toString()); - roles.add(dn); - } - return roles; - } catch (Exception e) { - throw new UserDirectoryException("Cannot get members", e); - } - } - - @Override - public Role[] getRequiredMembers() { - throw new UnsupportedOperationException(); - } - - @Override - public int getType() { - return GROUP; - } -} diff --git a/org.argeo.security.core/src/org/argeo/osgi/useradmin/LdifName.java b/org.argeo.security.core/src/org/argeo/osgi/useradmin/LdifName.java deleted file mode 100644 index 919e5073f..000000000 --- a/org.argeo.security.core/src/org/argeo/osgi/useradmin/LdifName.java +++ /dev/null @@ -1,26 +0,0 @@ -package org.argeo.osgi.useradmin; - -import javax.naming.ldap.LdapName; - -/** - * Standard LDAP attributes and object classes leveraged in this implementation - * of user admin. Named {@link LdifName} in order not to collide with - * {@link LdapName}. - */ -public enum LdifName { - // Attributes - dn, dc, cn, sn, uid, mail, displayName, objectClass, userPassword, givenName, description, member, - // Object classes - inetOrgPerson, organizationalPerson, person, groupOfNames, groupOfUniqueNames, top; - - public final static String PREFIX = "ldap:"; - - /** For use as XML name. */ - public String property() { - return PREFIX + name(); - } - - public static LdifName local(String property) { - return LdifName.valueOf(property.substring(PREFIX.length())); - } -} diff --git a/org.argeo.security.core/src/org/argeo/osgi/useradmin/LdifUser.java b/org.argeo.security.core/src/org/argeo/osgi/useradmin/LdifUser.java deleted file mode 100644 index 866c48c6b..000000000 --- a/org.argeo.security.core/src/org/argeo/osgi/useradmin/LdifUser.java +++ /dev/null @@ -1,363 +0,0 @@ -package org.argeo.osgi.useradmin; - -import java.io.UnsupportedEncodingException; -import java.nio.ByteBuffer; -import java.nio.CharBuffer; -import java.nio.charset.Charset; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Base64; -import java.util.Collections; -import java.util.Dictionary; -import java.util.Enumeration; -import java.util.HashSet; -import java.util.Iterator; -import java.util.List; -import java.util.Set; - -import javax.naming.NamingEnumeration; -import javax.naming.NamingException; -import javax.naming.directory.Attribute; -import javax.naming.directory.Attributes; -import javax.naming.directory.BasicAttribute; -import javax.naming.ldap.LdapName; - -/** Directory user implementation */ -class LdifUser implements DirectoryUser { - private final AbstractUserDirectory userAdmin; - - private final LdapName dn; - - private final boolean frozen; - private Attributes publishedAttributes; - - private final AttributeDictionary properties; - private final AttributeDictionary credentials; - - LdifUser(AbstractUserDirectory userAdmin, LdapName dn, Attributes attributes) { - this(userAdmin, dn, attributes, false); - } - - private LdifUser(AbstractUserDirectory userAdmin, LdapName dn, Attributes attributes, boolean frozen) { - this.userAdmin = userAdmin; - this.dn = dn; - this.publishedAttributes = attributes; - properties = new AttributeDictionary(false); - credentials = new AttributeDictionary(true); - this.frozen = frozen; - } - - @Override - public String getName() { - return dn.toString(); - } - - @Override - public int getType() { - return USER; - } - - @Override - public Dictionary getProperties() { - return properties; - } - - @Override - public Dictionary getCredentials() { - return credentials; - } - - @Override - public boolean hasCredential(String key, Object value) { - if (key == null) { - // TODO check other sources (like PKCS12) - char[] password = toChars(value); - byte[] hashedPassword = hash(password); - return hasCredential(LdifName.userPassword.name(), hashedPassword); - } - - Object storedValue = getCredentials().get(key); - if (storedValue == null || value == null) - return false; - if (!(value instanceof String || value instanceof byte[])) - return false; - if (storedValue instanceof String && value instanceof String) - return storedValue.equals(value); - if (storedValue instanceof byte[] && value instanceof byte[]) - return Arrays.equals((byte[]) storedValue, (byte[]) value); - return false; - } - - /** Hash and clear the password */ - private byte[] hash(char[] password) { - byte[] hashedPassword = ("{SHA}" + Base64.getEncoder().encodeToString(DigestUtils.sha1(toBytes(password)))) - .getBytes(); - Arrays.fill(password, '\u0000'); - return hashedPassword; - } - - private byte[] toBytes(char[] chars) { - CharBuffer charBuffer = CharBuffer.wrap(chars); - ByteBuffer byteBuffer = Charset.forName("UTF-8").encode(charBuffer); - byte[] bytes = Arrays.copyOfRange(byteBuffer.array(), byteBuffer.position(), byteBuffer.limit()); - Arrays.fill(charBuffer.array(), '\u0000'); // clear sensitive data - Arrays.fill(byteBuffer.array(), (byte) 0); // clear sensitive data - return bytes; - } - - private char[] toChars(Object obj) { - if (obj instanceof char[]) - return (char[]) obj; - if (!(obj instanceof byte[])) - throw new IllegalArgumentException(obj.getClass() + " is not a byte array"); - ByteBuffer fromBuffer = ByteBuffer.wrap((byte[]) obj); - CharBuffer toBuffer = Charset.forName("UTF-8").decode(fromBuffer); - char[] res = Arrays.copyOfRange(toBuffer.array(), toBuffer.position(), toBuffer.limit()); - Arrays.fill(fromBuffer.array(), (byte) 0); // clear sensitive data - Arrays.fill((byte[]) obj, (byte) 0); // clear sensitive data - Arrays.fill(toBuffer.array(), '\u0000'); // clear sensitive data - return res; - } - - @Override - public LdapName getDn() { - return dn; - } - - @Override - public synchronized Attributes getAttributes() { - return isEditing() ? getModifiedAttributes() : publishedAttributes; - } - - /** Should only be called from working copy thread. */ - private synchronized Attributes getModifiedAttributes() { - assert getWc() != null; - return getWc().getAttributes(getDn()); - } - - protected synchronized boolean isEditing() { - return getWc() != null && getModifiedAttributes() != null; - } - - private synchronized UserDirectoryWorkingCopy getWc() { - return userAdmin.getWorkingCopy(); - } - - protected synchronized void startEditing() { - if (frozen) - throw new UserDirectoryException("Cannot edit frozen view"); - if (getUserAdmin().isReadOnly()) - throw new UserDirectoryException("User directory is read-only"); - assert getModifiedAttributes() == null; - getWc().startEditing(this); - // modifiedAttributes = (Attributes) publishedAttributes.clone(); - } - - public synchronized void publishAttributes(Attributes modifiedAttributes) { - publishedAttributes = modifiedAttributes; - } - - public DirectoryUser getPublished() { - return new LdifUser(userAdmin, dn, publishedAttributes, true); - } - - @Override - public int hashCode() { - return dn.hashCode(); - } - - @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (obj instanceof LdifUser) { - LdifUser that = (LdifUser) obj; - return this.dn.equals(that.dn); - } - return false; - } - - @Override - public String toString() { - return dn.toString(); - } - - protected AbstractUserDirectory getUserAdmin() { - return userAdmin; - } - - private class AttributeDictionary extends Dictionary { - private final List effectiveKeys = new ArrayList(); - private final List attrFilter; - private final Boolean includeFilter; - - public AttributeDictionary(Boolean includeFilter) { - this.attrFilter = userAdmin.getCredentialAttributeIds(); - this.includeFilter = includeFilter; - try { - NamingEnumeration ids = getAttributes().getIDs(); - while (ids.hasMore()) { - String id = ids.next(); - if (includeFilter && attrFilter.contains(id)) - effectiveKeys.add(id); - else if (!includeFilter && !attrFilter.contains(id)) - effectiveKeys.add(id); - } - } catch (NamingException e) { - throw new UserDirectoryException("Cannot initialise attribute dictionary", e); - } - } - - @Override - public int size() { - return effectiveKeys.size(); - } - - @Override - public boolean isEmpty() { - return effectiveKeys.size() == 0; - } - - @Override - public Enumeration keys() { - return Collections.enumeration(effectiveKeys); - } - - @Override - public Enumeration elements() { - final Iterator it = effectiveKeys.iterator(); - return new Enumeration() { - - @Override - public boolean hasMoreElements() { - return it.hasNext(); - } - - @Override - public Object nextElement() { - String key = it.next(); - return get(key); - } - - }; - } - - @Override - public Object get(Object key) { - try { - Attribute attr = getAttributes().get(key.toString()); - if (attr == null) - return null; - Object value = attr.get(); - if (value instanceof byte[]) { - if (key.equals(LdifName.userPassword.name())) - // TODO other cases (certificates, images) - return value; - value = new String((byte[]) value, Charset.forName("UTF-8")); - } - if (attr.size() == 1) - return value; - if (!attr.getID().equals(LdifName.objectClass.name())) - return value; - // special case for object class - NamingEnumeration en = attr.getAll(); - Set objectClasses = new HashSet(); - while (en.hasMore()) { - String objectClass = en.next().toString(); - objectClasses.add(objectClass); - } - - if (objectClasses.contains(userAdmin.getUserObjectClass())) - return userAdmin.getUserObjectClass(); - else if (objectClasses.contains(userAdmin.getGroupObjectClass())) - return userAdmin.getGroupObjectClass(); - else - return value; - } catch (NamingException e) { - throw new UserDirectoryException("Cannot get value for attribute " + key, e); - } - } - - @Override - public Object put(String key, Object value) { - if (key == null) { - // TODO persist to other sources (like PKCS12) - char[] password = toChars(value); - byte[] hashedPassword = hash(password); - return put(LdifName.userPassword.name(), hashedPassword); - } - - userAdmin.checkEdit(); - if (!isEditing()) - startEditing(); - - if (!(value instanceof String || value instanceof byte[])) - throw new IllegalArgumentException("Value must be String or byte[]"); - - if (includeFilter && !attrFilter.contains(key)) - throw new IllegalArgumentException("Key " + key + " not included"); - else if (!includeFilter && attrFilter.contains(key)) - throw new IllegalArgumentException("Key " + key + " excluded"); - - try { - Attribute attribute = getModifiedAttributes().get(key.toString()); - attribute = new BasicAttribute(key.toString()); - if (value instanceof String && !isAsciiPrintable(((String) value))) - try { - attribute.add(((String) value).getBytes("UTF-8")); - } catch (UnsupportedEncodingException e) { - throw new UserDirectoryException("Cannot encode " + value, e); - } - else - attribute.add(value); - Attribute previousAttribute = getModifiedAttributes().put(attribute); - if (previousAttribute != null) - return previousAttribute.get(); - else - return null; - } catch (NamingException e) { - throw new UserDirectoryException("Cannot get value for attribute " + key, e); - } - } - - @Override - public Object remove(Object key) { - userAdmin.checkEdit(); - if (!isEditing()) - startEditing(); - - if (includeFilter && !attrFilter.contains(key)) - throw new IllegalArgumentException("Key " + key + " not included"); - else if (!includeFilter && attrFilter.contains(key)) - throw new IllegalArgumentException("Key " + key + " excluded"); - - try { - Attribute attr = getModifiedAttributes().remove(key.toString()); - if (attr != null) - return attr.get(); - else - return null; - } catch (NamingException e) { - throw new UserDirectoryException("Cannot remove attribute " + key, e); - } - } - } - - private static boolean isAsciiPrintable(String str) { - if (str == null) { - return false; - } - int sz = str.length(); - for (int i = 0; i < sz; i++) { - if (isAsciiPrintable(str.charAt(i)) == false) { - return false; - } - } - return true; - } - - private static boolean isAsciiPrintable(char ch) { - return ch >= 32 && ch < 127; - } - -} diff --git a/org.argeo.security.core/src/org/argeo/osgi/useradmin/LdifUserAdmin.java b/org.argeo.security.core/src/org/argeo/osgi/useradmin/LdifUserAdmin.java deleted file mode 100644 index 521ae8bb6..000000000 --- a/org.argeo.security.core/src/org/argeo/osgi/useradmin/LdifUserAdmin.java +++ /dev/null @@ -1,238 +0,0 @@ -package org.argeo.osgi.useradmin; - -import static org.argeo.osgi.useradmin.LdifName.inetOrgPerson; -import static org.argeo.osgi.useradmin.LdifName.objectClass; - -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.util.ArrayList; -import java.util.Dictionary; -import java.util.HashSet; -import java.util.Hashtable; -import java.util.List; -import java.util.Set; -import java.util.SortedMap; -import java.util.TreeMap; - -import javax.naming.NamingEnumeration; -import javax.naming.directory.Attributes; -import javax.naming.ldap.LdapName; -import javax.transaction.TransactionManager; - -import org.argeo.util.naming.LdifParser; -import org.argeo.util.naming.LdifWriter; -import org.osgi.framework.Filter; -import org.osgi.service.useradmin.Role; - -/** - * A user admin based on a LDIF files. Requires a {@link TransactionManager} and - * an open transaction for write access. - */ -public class LdifUserAdmin extends AbstractUserDirectory { - private SortedMap users = new TreeMap(); - private SortedMap groups = new TreeMap(); - - public LdifUserAdmin(String uri, String baseDn) { - this(fromUri(uri, baseDn)); - } - - public LdifUserAdmin(Dictionary properties) { - super(properties); - } - - public LdifUserAdmin(InputStream in) { - super(new Hashtable()); - load(in); - } - - private static Dictionary fromUri(String uri, String baseDn) { - Hashtable res = new Hashtable(); - res.put(UserAdminConf.uri.name(), uri); - res.put(UserAdminConf.baseDn.name(), baseDn); - return res; - } - - public void init() { - try { - if (getUri().getScheme().equals("file")) { - File file = new File(getUri()); - if (!file.exists()) - return; - } - load(getUri().toURL().openStream()); - } catch (Exception e) { - throw new UserDirectoryException("Cannot open URL " + getUri(), e); - } - } - - public void save() { - if (getUri() == null) - throw new UserDirectoryException("Cannot save LDIF user admin: no URI is set"); - if (isReadOnly()) - throw new UserDirectoryException("Cannot save LDIF user admin: " + getUri() + " is read-only"); - try (FileOutputStream out = new FileOutputStream(new File(getUri()))) { - save(out); - } catch (IOException e) { - throw new UserDirectoryException("Cannot save user admin to " + getUri(), e); - } - } - - public void save(OutputStream out) throws IOException { - try { - LdifWriter ldifWriter = new LdifWriter(out); - for (LdapName name : groups.keySet()) - ldifWriter.writeEntry(name, groups.get(name).getAttributes()); - for (LdapName name : users.keySet()) - ldifWriter.writeEntry(name, users.get(name).getAttributes()); - } finally { - out.close(); - } - } - - protected void load(InputStream in) { - try { - users.clear(); - groups.clear(); - - LdifParser ldifParser = new LdifParser(); - SortedMap allEntries = ldifParser.read(in); - for (LdapName key : allEntries.keySet()) { - Attributes attributes = allEntries.get(key); - // check for inconsistency - Set lowerCase = new HashSet(); - NamingEnumeration ids = attributes.getIDs(); - while (ids.hasMoreElements()) { - String id = ids.nextElement().toLowerCase(); - if (lowerCase.contains(id)) - throw new UserDirectoryException(key + " has duplicate id " + id); - lowerCase.add(id); - } - - // analyse object classes - NamingEnumeration objectClasses = attributes.get(objectClass.name()).getAll(); - // System.out.println(key); - objectClasses: while (objectClasses.hasMore()) { - String objectClass = objectClasses.next().toString(); - // System.out.println(" " + objectClass); - if (objectClass.equals(inetOrgPerson.name())) { - users.put(key, new LdifUser(this, key, attributes)); - break objectClasses; - } else if (objectClass.equals(getGroupObjectClass())) { - groups.put(key, new LdifGroup(this, key, attributes)); - break objectClasses; - } - } - } - } catch (Exception e) { - throw new UserDirectoryException("Cannot load user admin service from LDIF", e); - } - } - - public void destroy() { - if (users == null || groups == null) - throw new UserDirectoryException("User directory " + getBaseDn() + " is already destroyed"); - users.clear(); - users = null; - groups.clear(); - groups = null; - } - - protected DirectoryUser daoGetRole(LdapName key) { - if (groups.containsKey(key)) - return groups.get(key); - if (users.containsKey(key)) - return users.get(key); - return null; - } - - protected Boolean daoHasRole(LdapName dn) { - return users.containsKey(dn) || groups.containsKey(dn); - } - - @SuppressWarnings("unchecked") - protected List doGetRoles(Filter f) { - ArrayList res = new ArrayList(); - if (f == null) { - res.addAll(users.values()); - res.addAll(groups.values()); - } else { - for (DirectoryUser user : users.values()) { - // System.out.println("\n" + user.getName()); - // Dictionary props = user.getProperties(); - // for (Enumeration keys = props.keys(); keys - // .hasMoreElements();) { - // String key = keys.nextElement(); - // System.out.println(" " + key + "=" + props.get(key)); - // } - if (f.match(user.getProperties())) - res.add(user); - } - for (DirectoryUser group : groups.values()) - if (f.match(group.getProperties())) - res.add(group); - } - return res; - } - - @Override - protected List getDirectGroups(LdapName dn) { - List directGroups = new ArrayList(); - for (LdapName name : groups.keySet()) { - DirectoryGroup group = groups.get(name); - if (group.getMemberNames().contains(dn)) - directGroups.add(group.getDn()); - } - return directGroups; - } - - @Override - protected void prepare(UserDirectoryWorkingCopy wc) { - // delete - for (LdapName dn : wc.getDeletedUsers().keySet()) { - if (users.containsKey(dn)) - users.remove(dn); - else if (groups.containsKey(dn)) - groups.remove(dn); - else - throw new UserDirectoryException("User to delete not found " + dn); - } - // add - for (LdapName dn : wc.getNewUsers().keySet()) { - DirectoryUser user = wc.getNewUsers().get(dn); - if (users.containsKey(dn) || groups.containsKey(dn)) - throw new UserDirectoryException("User to create found " + dn); - else if (Role.USER == user.getType()) - users.put(dn, user); - else if (Role.GROUP == user.getType()) - groups.put(dn, (DirectoryGroup) user); - else - throw new UserDirectoryException("Unsupported role type " + user.getType() + " for new user " + dn); - } - // modify - for (LdapName dn : wc.getModifiedUsers().keySet()) { - Attributes modifiedAttrs = wc.getModifiedUsers().get(dn); - DirectoryUser user; - if (users.containsKey(dn)) - user = users.get(dn); - else if (groups.containsKey(dn)) - user = groups.get(dn); - else - throw new UserDirectoryException("User to modify no found " + dn); - user.publishAttributes(modifiedAttrs); - } - } - - @Override - protected void commit(UserDirectoryWorkingCopy wc) { - save(); - } - - @Override - protected void rollback(UserDirectoryWorkingCopy wc) { - init(); - } - -} diff --git a/org.argeo.security.core/src/org/argeo/osgi/useradmin/UserAdminConf.java b/org.argeo.security.core/src/org/argeo/osgi/useradmin/UserAdminConf.java deleted file mode 100644 index 316941ea8..000000000 --- a/org.argeo.security.core/src/org/argeo/osgi/useradmin/UserAdminConf.java +++ /dev/null @@ -1,193 +0,0 @@ -package org.argeo.osgi.useradmin; - -import java.io.UnsupportedEncodingException; -import java.net.URI; -import java.net.URISyntaxException; -import java.net.URLDecoder; -import java.util.Dictionary; -import java.util.Enumeration; -import java.util.Hashtable; -import java.util.LinkedHashMap; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; - -import javax.naming.Context; - -import org.osgi.framework.Constants; - -/** Properties used to configure user admins. */ -public enum UserAdminConf { - /** Base DN (cannot be configured externally) */ - baseDn("dc=example,dc=com"), - - /** URI of the underlying resource (cannot be configured externally) */ - uri("ldap://localhost:10389"), - - /** User objectClass */ - userObjectClass("inetOrgPerson"), - - /** Relative base DN for users */ - userBase("ou=People"), - - /** Groups objectClass */ - groupObjectClass("groupOfNames"), - - /** Relative base DN for users */ - groupBase("ou=Groups"), - - /** Read-only source */ - readOnly(null); - - public final static String FACTORY_PID = "org.argeo.osgi.useradmin.config"; - - /** The default value. */ - private Object def; - - UserAdminConf(Object def) { - this.def = def; - } - - public Object getDefault() { - return def; - } - - /** - * For use as Java property. - * - * @deprecated use {@link #name()} instead - */ - @Deprecated - public String property() { - return name(); - } - - public String getValue(Dictionary properties) { - Object res = getRawValue(properties); - if (res == null) - return null; - return res.toString(); - } - - @SuppressWarnings("unchecked") - public T getRawValue(Dictionary properties) { - Object res = properties.get(name()); - if (res == null) - res = getDefault(); - return (T) res; - } - - /** @deprecated use {@link #valueOf(String)} instead */ - @Deprecated - public static UserAdminConf local(String property) { - return UserAdminConf.valueOf(property); - } - - /** Hides host and credentials. */ - public static URI propertiesAsUri(Dictionary properties) { - StringBuilder query = new StringBuilder(); - - boolean first = true; - for (Enumeration keys = properties.keys(); keys.hasMoreElements();) { - String key = keys.nextElement(); - // TODO clarify which keys are relevant (list only the enum?) - if (!key.equals("service.factoryPid") && !key.equals("cn") && !key.equals("dn") - && !key.equals(Constants.SERVICE_PID) && !key.startsWith("java") && !key.equals(baseDn.name()) - && !key.equals(uri.name())) { - if (first) - first = false; - else - query.append('&'); - query.append(valueOf(key).name()); - query.append('=').append(properties.get(key).toString()); - } - } - - String bDn = (String) properties.get(baseDn.name()); - try { - return new URI(null, null, bDn != null ? '/' + bDn : null, query.length() != 0 ? query.toString() : null, - null); - } catch (URISyntaxException e) { - throw new UserDirectoryException("Cannot create URI from properties", e); - } - } - - public static Dictionary uriAsProperties(String uriStr) { - try { - Hashtable res = new Hashtable(); - URI u = new URI(uriStr); - String scheme = u.getScheme(); - String path = u.getPath(); - String bDn = path.substring(path.lastIndexOf('/') + 1, path.length()); - if (bDn.endsWith(".ldif")) - bDn = bDn.substring(0, bDn.length() - ".ldif".length()); - - String principal = null; - String credentials = null; - if (scheme != null) - if (scheme.equals("ldap") || scheme.equals("ldaps")) { - // TODO additional checks - String[] userInfo = u.getUserInfo().split(":"); - principal = userInfo.length > 0 ? userInfo[0] : null; - credentials = userInfo.length > 1 ? userInfo[1] : null; - } else if (scheme.equals("file")) { - } else - throw new UserDirectoryException("Unsupported scheme " + scheme); - Map> query = splitQuery(u.getQuery()); - for (String key : query.keySet()) { - UserAdminConf ldapProp = UserAdminConf.valueOf(key); - List values = query.get(key); - if (values.size() == 1) { - res.put(ldapProp.name(), values.get(0)); - } else { - throw new UserDirectoryException("Only single values are supported"); - } - } - res.put(baseDn.name(), bDn); - if (principal != null) - res.put(Context.SECURITY_PRINCIPAL, principal); - if (credentials != null) - res.put(Context.SECURITY_CREDENTIALS, credentials); - if (scheme != null) { - URI bareUri = new URI(scheme, null, u.getHost(), u.getPort(), - scheme.equals("file") ? u.getPath() : null, null, null); - res.put(uri.name(), bareUri.toString()); - } - return res; - } catch (Exception e) { - throw new UserDirectoryException("Cannot convert " + uri + " to properties", e); - } - } - - private static Map> splitQuery(String query) throws UnsupportedEncodingException { - final Map> query_pairs = new LinkedHashMap>(); - if (query == null) - return query_pairs; - final String[] pairs = query.split("&"); - for (String pair : pairs) { - final int idx = pair.indexOf("="); - final String key = idx > 0 ? URLDecoder.decode(pair.substring(0, idx), "UTF-8") : pair; - if (!query_pairs.containsKey(key)) { - query_pairs.put(key, new LinkedList()); - } - final String value = idx > 0 && pair.length() > idx + 1 - ? URLDecoder.decode(pair.substring(idx + 1), "UTF-8") : null; - query_pairs.get(key).add(value); - } - return query_pairs; - } - - public static void main(String[] args) { - Dictionary props = uriAsProperties("ldap://" + "uid=admin,ou=system:secret@localhost:10389" - + "/dc=example,dc=com" + "?readOnly=false&userObjectClass=person"); - System.out.println(props); - System.out.println(propertiesAsUri(props)); - - System.out.println(uriAsProperties("file://some/dir/dc=example,dc=com.ldif")); - - props = uriAsProperties( - "/dc=example,dc=com.ldif?readOnly=true" + "&userBase=ou=CoWorkers,ou=People&groupBase=ou=Roles"); - System.out.println(props); - System.out.println(propertiesAsUri(props)); - } -} diff --git a/org.argeo.security.core/src/org/argeo/osgi/useradmin/UserDirectory.java b/org.argeo.security.core/src/org/argeo/osgi/useradmin/UserDirectory.java deleted file mode 100644 index e5de73836..000000000 --- a/org.argeo.security.core/src/org/argeo/osgi/useradmin/UserDirectory.java +++ /dev/null @@ -1,23 +0,0 @@ -package org.argeo.osgi.useradmin; - -import javax.naming.ldap.LdapName; -import javax.transaction.xa.XAResource; - -/** Information about a user directory. */ -public interface UserDirectory { - /** The base DN of all entries in this user directory */ - public LdapName getBaseDn(); - - /** The related {@link XAResource} */ - public XAResource getXaResource(); - - public boolean isReadOnly(); - - public String getUserObjectClass(); - - public String getUserBase(); - - public String getGroupObjectClass(); - - public String getGroupBase(); -} diff --git a/org.argeo.security.core/src/org/argeo/osgi/useradmin/UserDirectoryException.java b/org.argeo.security.core/src/org/argeo/osgi/useradmin/UserDirectoryException.java deleted file mode 100644 index 613d0fdf0..000000000 --- a/org.argeo.security.core/src/org/argeo/osgi/useradmin/UserDirectoryException.java +++ /dev/null @@ -1,19 +0,0 @@ -package org.argeo.osgi.useradmin; - -import org.osgi.service.useradmin.UserAdmin; - -/** - * Exceptions related to Argeo's implementation of OSGi {@link UserAdmin} - * service. - */ -public class UserDirectoryException extends RuntimeException { - private static final long serialVersionUID = 1419352360062048603L; - - public UserDirectoryException(String message) { - super(message); - } - - public UserDirectoryException(String message, Throwable e) { - super(message, e); - } -} diff --git a/org.argeo.security.core/src/org/argeo/osgi/useradmin/UserDirectoryWorkingCopy.java b/org.argeo.security.core/src/org/argeo/osgi/useradmin/UserDirectoryWorkingCopy.java deleted file mode 100644 index 0e25bdfa1..000000000 --- a/org.argeo.security.core/src/org/argeo/osgi/useradmin/UserDirectoryWorkingCopy.java +++ /dev/null @@ -1,58 +0,0 @@ -package org.argeo.osgi.useradmin; - -import java.util.HashMap; -import java.util.Map; - -import javax.naming.directory.Attributes; -import javax.naming.ldap.LdapName; -import javax.transaction.xa.XAResource; - -/** {@link XAResource} for a user directory being edited. */ -class UserDirectoryWorkingCopy { - // private final static Log log = LogFactory - // .getLog(UserDirectoryWorkingCopy.class); - - private Map newUsers = new HashMap(); - private Map modifiedUsers = new HashMap(); - private Map deletedUsers = new HashMap(); - - void cleanUp() { - // clean collections - newUsers.clear(); - newUsers = null; - modifiedUsers.clear(); - modifiedUsers = null; - deletedUsers.clear(); - deletedUsers = null; - } - - public boolean noModifications() { - return newUsers.size() == 0 && modifiedUsers.size() == 0 - && deletedUsers.size() == 0; - } - - public Attributes getAttributes(LdapName dn) { - if (modifiedUsers.containsKey(dn)) - return modifiedUsers.get(dn); - return null; - } - - public void startEditing(DirectoryUser user) { - LdapName dn = user.getDn(); - if (modifiedUsers.containsKey(dn)) - throw new UserDirectoryException("Already editing " + dn); - modifiedUsers.put(dn, (Attributes) user.getAttributes().clone()); - } - - public Map getNewUsers() { - return newUsers; - } - - public Map getDeletedUsers() { - return deletedUsers; - } - - public Map getModifiedUsers() { - return modifiedUsers; - } -} diff --git a/org.argeo.security.core/src/org/argeo/osgi/useradmin/WcXaResource.java b/org.argeo.security.core/src/org/argeo/osgi/useradmin/WcXaResource.java deleted file mode 100644 index a6048fdca..000000000 --- a/org.argeo.security.core/src/org/argeo/osgi/useradmin/WcXaResource.java +++ /dev/null @@ -1,143 +0,0 @@ -package org.argeo.osgi.useradmin; - -import java.util.HashMap; -import java.util.Map; - -import javax.transaction.xa.XAException; -import javax.transaction.xa.XAResource; -import javax.transaction.xa.Xid; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - -/** {@link XAResource} for a user directory being edited. */ -class WcXaResource implements XAResource { - private final static Log log = LogFactory.getLog(WcXaResource.class); - - private final AbstractUserDirectory userDirectory; - - private Map workingCopies = new HashMap(); - private Xid editingXid = null; - private int transactionTimeout = 0; - - public WcXaResource(AbstractUserDirectory userDirectory) { - this.userDirectory = userDirectory; - } - - @Override - public synchronized void start(Xid xid, int flags) throws XAException { - if (editingXid != null) - throw new UserDirectoryException("Already editing " + editingXid); - UserDirectoryWorkingCopy wc = workingCopies.put(xid, - new UserDirectoryWorkingCopy()); - if (wc != null) - throw new UserDirectoryException( - "There is already a working copy for " + xid); - this.editingXid = xid; - } - - @Override - public void end(Xid xid, int flags) throws XAException { - checkXid(xid); - } - - private UserDirectoryWorkingCopy wc(Xid xid) { - return workingCopies.get(xid); - } - - synchronized UserDirectoryWorkingCopy wc() { - if (editingXid == null) - return null; - UserDirectoryWorkingCopy wc = workingCopies.get(editingXid); - if (wc == null) - throw new UserDirectoryException("No working copy found for " - + editingXid); - return wc; - } - - private synchronized void cleanUp(Xid xid) { - wc(xid).cleanUp(); - workingCopies.remove(xid); - editingXid = null; - } - - @Override - public int prepare(Xid xid) throws XAException { - checkXid(xid); - UserDirectoryWorkingCopy wc = wc(xid); - if (wc.noModifications()) - return XA_RDONLY; - try { - userDirectory.prepare(wc); - } catch (Exception e) { - log.error("Cannot prepare " + xid, e); - throw new XAException(XAException.XAER_RMERR); - } - return XA_OK; - } - - @Override - public void commit(Xid xid, boolean onePhase) throws XAException { - try { - checkXid(xid); - UserDirectoryWorkingCopy wc = wc(xid); - if (wc.noModifications()) - return; - if (onePhase) - userDirectory.prepare(wc); - userDirectory.commit(wc); - } catch (Exception e) { - log.error("Cannot commit " + xid, e); - throw new XAException(XAException.XAER_RMERR); - } finally { - cleanUp(xid); - } - } - - @Override - public void rollback(Xid xid) throws XAException { - try { - checkXid(xid); - userDirectory.rollback(wc(xid)); - } catch (Exception e) { - log.error("Cannot rollback " + xid, e); - throw new XAException(XAException.XAER_RMERR); - } finally { - cleanUp(xid); - } - } - - @Override - public void forget(Xid xid) throws XAException { - throw new UnsupportedOperationException(); - } - - @Override - public boolean isSameRM(XAResource xares) throws XAException { - return xares == this; - } - - @Override - public Xid[] recover(int flag) throws XAException { - return new Xid[0]; - } - - @Override - public int getTransactionTimeout() throws XAException { - return transactionTimeout; - } - - @Override - public boolean setTransactionTimeout(int seconds) throws XAException { - transactionTimeout = seconds; - return true; - } - - private void checkXid(Xid xid) throws XAException { - if (xid == null) - throw new XAException(XAException.XAER_OUTSIDE); - if (!xid.equals(xid)) - throw new XAException(XAException.XAER_NOTA); - } - -} diff --git a/org.argeo.security.core/src/org/argeo/util/naming/AttributesDictionary.java b/org.argeo.security.core/src/org/argeo/util/naming/AttributesDictionary.java deleted file mode 100644 index c211e8e8a..000000000 --- a/org.argeo.security.core/src/org/argeo/util/naming/AttributesDictionary.java +++ /dev/null @@ -1,171 +0,0 @@ -package org.argeo.util.naming; - -import java.util.Dictionary; -import java.util.Enumeration; - -import javax.naming.NamingEnumeration; -import javax.naming.NamingException; -import javax.naming.directory.Attribute; -import javax.naming.directory.Attributes; -import javax.naming.directory.BasicAttribute; - -public class AttributesDictionary extends Dictionary { - private final Attributes attributes; - - /** The provided attributes is wrapped, not copied. */ - public AttributesDictionary(Attributes attributes) { - if (attributes == null) - throw new IllegalArgumentException("Attributes cannot be null"); - this.attributes = attributes; - } - - @Override - public int size() { - return attributes.size(); - } - - @Override - public boolean isEmpty() { - return attributes.size() == 0; - } - - @Override - public Enumeration keys() { - NamingEnumeration namingEnumeration = attributes.getIDs(); - return new Enumeration() { - - @Override - public boolean hasMoreElements() { - return namingEnumeration.hasMoreElements(); - } - - @Override - public String nextElement() { - return namingEnumeration.nextElement(); - } - - }; - } - - @Override - public Enumeration elements() { - NamingEnumeration namingEnumeration = attributes.getIDs(); - return new Enumeration() { - - @Override - public boolean hasMoreElements() { - return namingEnumeration.hasMoreElements(); - } - - @Override - public Object nextElement() { - String key = namingEnumeration.nextElement(); - return get(key); - } - - }; - } - - @Override - /** @returns a String or String[] */ - public Object get(Object key) { - try { - if (key == null) - throw new IllegalArgumentException("Key cannot be null"); - Attribute attr = attributes.get(key.toString()); - if (attr == null) - return null; - if (attr.size() == 0) - throw new IllegalStateException("There must be at least one value"); - else if (attr.size() == 1) { - return attr.get().toString(); - } else {// multiple - String[] res = new String[attr.size()]; - for (int i = 0; i < attr.size(); i++) { - Object value = attr.get(); - if (value == null) - throw new RuntimeException("Values cannot be null"); - res[i] = attr.get(i).toString(); - } - return res; - } - } catch (NamingException e) { - throw new RuntimeException("Cannot get value for " + key, e); - } - } - - @Override - public Object put(String key, Object value) { - if (key == null) - throw new IllegalArgumentException("Key cannot be null"); - if (value == null) - throw new IllegalArgumentException("Value cannot be null"); - - Object oldValue = get(key); - Attribute attr = attributes.get(key); - if (attr == null) { - attr = new BasicAttribute(key); - attributes.put(attr); - } - - if (value instanceof String[]) { - String[] values = (String[]) value; - // clean additional values - for (int i = values.length; i < attr.size(); i++) - attr.remove(i); - // set values - for (int i = 0; i < values.length; i++) { - attr.set(i, values[i]); - } - } else { - if (attr.size() > 1) - throw new IllegalArgumentException("Attribute " + key + " is multi-valued"); - if (attr.size() == 1) { - try { - if (!attr.get(0).equals(value)) - attr.set(0, value.toString()); - } catch (NamingException e) { - throw new RuntimeException("Cannot check existing value", e); - } - } else { - attr.add(value.toString()); - } - } - return oldValue; - } - - @Override - public Object remove(Object key) { - if (key == null) - throw new IllegalArgumentException("Key cannot be null"); - Object oldValue = get(key); - if (oldValue == null) - return null; - return attributes.remove(key.toString()); - } - - /** - * Copy the content of an {@link javax.naming.Attributes} to the - * provided {@link Dictionary}. - */ - public static void copy(Attributes attributes, Dictionary dictionary) { - AttributesDictionary ad = new AttributesDictionary(attributes); - Enumeration keys = ad.keys(); - while (keys.hasMoreElements()) { - String key = keys.nextElement(); - dictionary.put(key, ad.get(key)); - } - } - - /** - * Copy a {@link Dictionary} into an {@link javax.naming.Attributes}. - */ - public static void copy(Dictionary dictionary, Attributes attributes) { - AttributesDictionary ad = new AttributesDictionary(attributes); - Enumeration keys = dictionary.keys(); - while (keys.hasMoreElements()) { - String key = keys.nextElement(); - ad.put(key, dictionary.get(key)); - } - } -} diff --git a/org.argeo.security.core/src/org/argeo/util/naming/LdifParser.java b/org.argeo.security.core/src/org/argeo/util/naming/LdifParser.java deleted file mode 100644 index ec73e8acc..000000000 --- a/org.argeo.security.core/src/org/argeo/util/naming/LdifParser.java +++ /dev/null @@ -1,145 +0,0 @@ -package org.argeo.util.naming; - -import static org.argeo.osgi.useradmin.LdifName.dn; - -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.util.ArrayList; -import java.util.Base64; -import java.util.List; -import java.util.SortedMap; -import java.util.TreeMap; - -import javax.naming.InvalidNameException; -import javax.naming.NamingException; -import javax.naming.directory.Attribute; -import javax.naming.directory.Attributes; -import javax.naming.directory.BasicAttribute; -import javax.naming.directory.BasicAttributes; -import javax.naming.ldap.LdapName; -import javax.naming.ldap.Rdn; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.argeo.osgi.useradmin.UserDirectoryException; - -/** Basic LDIF parser. */ -public class LdifParser { - private final static Log log = LogFactory.getLog(LdifParser.class); - - protected Attributes addAttributes(SortedMap res, int lineNumber, LdapName currentDn, - Attributes currentAttributes) { - try { - Rdn nameRdn = currentDn.getRdn(currentDn.size() - 1); - Attribute nameAttr = currentAttributes.get(nameRdn.getType()); - if (nameAttr == null) - currentAttributes.put(nameRdn.getType(), nameRdn.getValue()); - else if (!nameAttr.get().equals(nameRdn.getValue())) - throw new UserDirectoryException( - "Attribute " + nameAttr.getID() + "=" + nameAttr.get() + " not consistent with DN " + currentDn - + " (shortly before line " + lineNumber + " in LDIF file)"); - Attributes previous = res.put(currentDn, currentAttributes); - if (log.isTraceEnabled()) - log.trace("Added " + currentDn); - return previous; - } catch (NamingException e) { - throw new UserDirectoryException("Cannot add " + currentDn, e); - } - } - - public SortedMap read(InputStream in) throws IOException { - SortedMap res = new TreeMap(); - try { - List lines = new ArrayList<>(); - try (BufferedReader br = new BufferedReader(new InputStreamReader(in))) { - String line; - while ((line = br.readLine()) != null) { - lines.add(line); - } - } - if (lines.size() == 0) - return res; - // add an empty new line since the last line is not checked - if (!lines.get(lines.size() - 1).equals("")) - lines.add(""); - - LdapName currentDn = null; - Attributes currentAttributes = null; - StringBuilder currentEntry = new StringBuilder(); - - readLines: for (int lineNumber = 0; lineNumber < lines.size(); lineNumber++) { - String line = lines.get(lineNumber); - boolean isLastLine = false; - if (lineNumber == lines.size() - 1) - isLastLine = true; - if (line.startsWith(" ")) { - currentEntry.append(line.substring(1)); - if (!isLastLine) - continue readLines; - } - - if (currentEntry.length() != 0 || isLastLine) { - // read previous attribute - StringBuilder attrId = new StringBuilder(8); - boolean isBase64 = false; - readAttrId: for (int i = 0; i < currentEntry.length(); i++) { - char c = currentEntry.charAt(i); - if (c == ':') { - if (i + 1 < currentEntry.length() && currentEntry.charAt(i + 1) == ':') - isBase64 = true; - currentEntry.delete(0, i + (isBase64 ? 2 : 1)); - break readAttrId; - } else { - attrId.append(c); - } - } - - String attributeId = attrId.toString(); - String cleanValueStr = currentEntry.toString().trim(); - Object attributeValue = isBase64 ? Base64.getDecoder().decode(cleanValueStr) : cleanValueStr; - - // manage DN attributes - if (attributeId.equals(dn.name()) || isLastLine) { - if (currentDn != null) { - // - // ADD - // - Attributes previous = addAttributes(res, lineNumber, currentDn, currentAttributes); - if (previous != null) { - log.warn("There was already an entry with DN " + currentDn - + ", which has been discarded by a subsequent one."); - } - } - - if (attributeId.equals(dn.name())) - try { - currentDn = new LdapName(attributeValue.toString()); - currentAttributes = new BasicAttributes(true); - } catch (InvalidNameException e) { - log.error(attributeValue + " not a valid DN, skipping the entry."); - currentDn = null; - currentAttributes = null; - } - } - - // store attribute - if (currentAttributes != null) { - Attribute attribute = currentAttributes.get(attributeId); - if (attribute == null) { - attribute = new BasicAttribute(attributeId); - currentAttributes.put(attribute); - } - attribute.add(attributeValue); - } - currentEntry = new StringBuilder(); - } - currentEntry.append(line); - } - } finally { - in.close(); - } - return res; - } -} \ No newline at end of file diff --git a/org.argeo.security.core/src/org/argeo/util/naming/LdifWriter.java b/org.argeo.security.core/src/org/argeo/util/naming/LdifWriter.java deleted file mode 100644 index 37d90b4e4..000000000 --- a/org.argeo.security.core/src/org/argeo/util/naming/LdifWriter.java +++ /dev/null @@ -1,77 +0,0 @@ -package org.argeo.util.naming; - -import static org.argeo.osgi.useradmin.LdifName.dn; - -import java.io.IOException; -import java.io.OutputStream; -import java.io.OutputStreamWriter; -import java.io.Writer; -import java.util.Base64; -import java.util.Map; - -import javax.naming.NamingEnumeration; -import javax.naming.NamingException; -import javax.naming.directory.Attribute; -import javax.naming.directory.Attributes; -import javax.naming.ldap.LdapName; -import javax.naming.ldap.Rdn; - -import org.argeo.osgi.useradmin.UserDirectoryException; - -/** Basic LDIF writer */ -public class LdifWriter { - private final Writer writer; - - /** Writer must be closed by caller */ - public LdifWriter(Writer writer) { - this.writer = writer; - } - - /** Stream must be closed by caller */ - public LdifWriter(OutputStream out) { - this(new OutputStreamWriter(out)); - } - - public void writeEntry(LdapName name, Attributes attributes) throws IOException { - try { - // check consistency - Rdn nameRdn = name.getRdn(name.size() - 1); - Attribute nameAttr = attributes.get(nameRdn.getType()); - if (!nameAttr.get().equals(nameRdn.getValue())) - throw new UserDirectoryException( - "Attribute " + nameAttr.getID() + "=" + nameAttr.get() + " not consistent with DN " + name); - - writer.append(dn.name() + ":").append(name.toString()).append('\n'); - Attribute objectClassAttr = attributes.get("objectClass"); - if (objectClassAttr != null) - writeAttribute(objectClassAttr); - for (NamingEnumeration attrs = attributes.getAll(); attrs.hasMore();) { - Attribute attribute = attrs.next(); - if (attribute.getID().equals(dn.name()) || attribute.getID().equals("objectClass")) - continue;// skip DN attribute - writeAttribute(attribute); - } - writer.append('\n'); - writer.flush(); - } catch (NamingException e) { - throw new UserDirectoryException("Cannot write LDIF", e); - } - } - - public void write(Map entries) throws IOException { - for (LdapName dn : entries.keySet()) - writeEntry(dn, entries.get(dn)); - } - - protected void writeAttribute(Attribute attribute) throws NamingException, IOException { - for (NamingEnumeration attrValues = attribute.getAll(); attrValues.hasMore();) { - Object value = attrValues.next(); - if (value instanceof byte[]) { - String encoded = Base64.getEncoder().encodeToString((byte[]) value); - writer.append(attribute.getID()).append("::").append(encoded).append('\n'); - } else { - writer.append(attribute.getID()).append(':').append(value.toString()).append('\n'); - } - } - } -} diff --git a/org.argeo.security.jackrabbit/.classpath b/org.argeo.security.jackrabbit/.classpath deleted file mode 100644 index a8a298a3c..000000000 --- a/org.argeo.security.jackrabbit/.classpath +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - diff --git a/org.argeo.security.jackrabbit/.project b/org.argeo.security.jackrabbit/.project deleted file mode 100644 index 35bcca28f..000000000 --- a/org.argeo.security.jackrabbit/.project +++ /dev/null @@ -1,28 +0,0 @@ - - - org.argeo.security.jackrabbit - - - - - - org.eclipse.jdt.core.javabuilder - - - - - org.eclipse.pde.ManifestBuilder - - - - - org.eclipse.pde.SchemaBuilder - - - - - - org.eclipse.pde.PluginNature - org.eclipse.jdt.core.javanature - - diff --git a/org.argeo.security.jackrabbit/bnd.bnd b/org.argeo.security.jackrabbit/bnd.bnd deleted file mode 100644 index 6bab2aa5e..000000000 --- a/org.argeo.security.jackrabbit/bnd.bnd +++ /dev/null @@ -1,3 +0,0 @@ -Fragment-Host: org.apache.jackrabbit.core -Import-Package: org.springframework.core,\ -* diff --git a/org.argeo.security.jackrabbit/build.properties b/org.argeo.security.jackrabbit/build.properties deleted file mode 100644 index 0cc2cd06c..000000000 --- a/org.argeo.security.jackrabbit/build.properties +++ /dev/null @@ -1,23 +0,0 @@ -source.. = src/,\ - ext/test/ - -additional.bundles = org.junit,\ - org.apache.jackrabbit.core,\ - javax.jcr,\ - org.apache.jackrabbit.api,\ - org.apache.jackrabbit.data,\ - org.apache.jackrabbit.jcr.commons,\ - org.apache.jackrabbit.spi,\ - org.apache.jackrabbit.spi.commons,\ - org.slf4j.api,\ - org.slf4j.commons.logging,\ - org.slf4j.log4j12,\ - org.apache.log4j,\ - org.apache.commons.collections,\ - EDU.oswego.cs.dl.util.concurrent,\ - org.apache.lucene,\ - org.apache.tika.core,\ - org.apache.commons.dbcp,\ - org.apache.commons.pool,\ - org.argeo.server.jcr - diff --git a/org.argeo.security.jackrabbit/ext/test/log4j.properties b/org.argeo.security.jackrabbit/ext/test/log4j.properties deleted file mode 100644 index b4edd7c2e..000000000 --- a/org.argeo.security.jackrabbit/ext/test/log4j.properties +++ /dev/null @@ -1,17 +0,0 @@ -log4j.rootLogger=WARN, console - -## Levels -log4j.logger.org.argeo=DEBUG -log4j.logger.org.apache.jackrabbit=OFF -log4j.logger.org.apache.jackrabbit.core.security=DEBUG -log4j.logger.org.apache.jackrabbit.core.DefaultSecurityManager=DEBUG - -## Appenders -# console is set to be a ConsoleAppender. -log4j.appender.console=org.apache.log4j.ConsoleAppender - -# console uses PatternLayout. -log4j.appender.console.layout=org.apache.log4j.PatternLayout -#log4j.appender.console.layout.ConversionPattern= %-5p %d{ISO8601} %m - %c%n -#log4j.appender.console.layout.ConversionPattern=%m%n -log4j.appender.console.layout.ConversionPattern=%d{ABSOLUTE} %m (%F:%L) [%t] %p %n diff --git a/org.argeo.security.jackrabbit/ext/test/org/argeo/security/jackrabbit/JackrabbitAuthTest.java b/org.argeo.security.jackrabbit/ext/test/org/argeo/security/jackrabbit/JackrabbitAuthTest.java deleted file mode 100644 index 47afff922..000000000 --- a/org.argeo.security.jackrabbit/ext/test/org/argeo/security/jackrabbit/JackrabbitAuthTest.java +++ /dev/null @@ -1,52 +0,0 @@ -package org.argeo.security.jackrabbit; - -import javax.jcr.Repository; -import javax.jcr.Session; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.argeo.jackrabbit.unit.AbstractJackrabbitTestCase; - -public class JackrabbitAuthTest extends AbstractJackrabbitTestCase { - private final Log log = LogFactory.getLog(JackrabbitAuthTest.class); - - public void testLogin() throws Exception { - Session session = session(); - log.debug(session.getUserID()); - assertEquals("admin", session.getUserID()); - // Subject subject = new Subject(); - // LoginContext loginContext = new LoginContext("SYSTEM", subject); - // loginContext.login(); - // Subject.doAs(subject, new PrivilegedExceptionAction() { - // - // @Override - // public Void run() throws Exception { - // Repository repository = getRepository(); - // Session session = repository.login(); - // log.debug(session.getUserID()); - // return null; - // } - // }); - } - - @Override - protected String getLoginContext() { - return LOGIN_CONTEXT_TEST_SYSTEM; - } - - @Override - protected Repository createRepository() throws Exception { - return super.createRepository(); - } - - @Override - protected void clearRepository(Repository repository) throws Exception { - // System.setProperty("java.security.auth.login.config", ""); - } - - @Override - protected String getRepositoryConfigResource() { - return "/org/argeo/security/jackrabbit/repository-memory-test.xml"; - } - -} diff --git a/org.argeo.security.jackrabbit/ext/test/org/argeo/security/jackrabbit/repository-memory-test.xml b/org.argeo.security.jackrabbit/ext/test/org/argeo/security/jackrabbit/repository-memory-test.xml deleted file mode 100644 index e28555574..000000000 --- a/org.argeo.security.jackrabbit/ext/test/org/argeo/security/jackrabbit/repository-memory-test.xml +++ /dev/null @@ -1,65 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/org.argeo.security.jackrabbit/pom.xml b/org.argeo.security.jackrabbit/pom.xml deleted file mode 100644 index 1dfc10325..000000000 --- a/org.argeo.security.jackrabbit/pom.xml +++ /dev/null @@ -1,27 +0,0 @@ - - - 4.0.0 - - org.argeo.commons - argeo-commons - 2.1.46-SNAPSHOT - .. - - org.argeo.security.jackrabbit - Commons Jackrabbit Extensions - - - org.argeo.commons - org.argeo.cms.api - 2.1.46-SNAPSHOT - - - - - org.argeo.commons - org.argeo.server.jcr - 2.1.46-SNAPSHOT - test - - - \ No newline at end of file diff --git a/org.argeo.security.jackrabbit/src/org/argeo/security/jackrabbit/ArgeoAccessControlProvider.java b/org.argeo.security.jackrabbit/src/org/argeo/security/jackrabbit/ArgeoAccessControlProvider.java deleted file mode 100644 index cd0cf86f2..000000000 --- a/org.argeo.security.jackrabbit/src/org/argeo/security/jackrabbit/ArgeoAccessControlProvider.java +++ /dev/null @@ -1,22 +0,0 @@ -package org.argeo.security.jackrabbit; - -import java.util.Map; - -import javax.jcr.RepositoryException; -import javax.jcr.Session; - -import org.apache.jackrabbit.core.security.authorization.acl.ACLProvider; - -/** Argeo specific access control provider */ -public class ArgeoAccessControlProvider extends ACLProvider { - - @SuppressWarnings({ "rawtypes", "unchecked" }) - @Override - public void init(Session systemSession, Map configuration) - throws RepositoryException { - if (!configuration.containsKey(PARAM_ALLOW_UNKNOWN_PRINCIPALS)) - configuration.put(PARAM_ALLOW_UNKNOWN_PRINCIPALS, "true"); - super.init(systemSession, configuration); - } - -} diff --git a/org.argeo.security.jackrabbit/src/org/argeo/security/jackrabbit/ArgeoAccessManager.java b/org.argeo.security.jackrabbit/src/org/argeo/security/jackrabbit/ArgeoAccessManager.java deleted file mode 100644 index 52ea3c984..000000000 --- a/org.argeo.security.jackrabbit/src/org/argeo/security/jackrabbit/ArgeoAccessManager.java +++ /dev/null @@ -1,50 +0,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. - */ -package org.argeo.security.jackrabbit; - -import javax.jcr.PathNotFoundException; -import javax.jcr.RepositoryException; -import javax.jcr.security.Privilege; - -import org.apache.jackrabbit.core.id.ItemId; -import org.apache.jackrabbit.core.security.DefaultAccessManager; -import org.apache.jackrabbit.spi.Path; - -/** - * Intermediary class in order to have a consistent naming in config files. Does - * nothing for the time being, but may in the future. - */ -public class ArgeoAccessManager extends DefaultAccessManager { - - @Override - public boolean canRead(Path itemPath, ItemId itemId) - throws RepositoryException { - return super.canRead(itemPath, itemId); - } - - @Override - public Privilege[] getPrivileges(String absPath) - throws PathNotFoundException, RepositoryException { - return super.getPrivileges(absPath); - } - - @Override - public boolean hasPrivileges(String absPath, Privilege[] privileges) - throws PathNotFoundException, RepositoryException { - return super.hasPrivileges(absPath, privileges); - } - -} diff --git a/org.argeo.security.jackrabbit/src/org/argeo/security/jackrabbit/ArgeoSecurityManager.java b/org.argeo.security.jackrabbit/src/org/argeo/security/jackrabbit/ArgeoSecurityManager.java deleted file mode 100644 index 978be436b..000000000 --- a/org.argeo.security.jackrabbit/src/org/argeo/security/jackrabbit/ArgeoSecurityManager.java +++ /dev/null @@ -1,108 +0,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. - */ -package org.argeo.security.jackrabbit; - -import java.security.Principal; -import java.util.Set; - -import javax.jcr.RepositoryException; -import javax.jcr.Session; -import javax.security.auth.Subject; -import javax.security.auth.x500.X500Principal; - -import org.apache.jackrabbit.api.security.user.UserManager; -import org.apache.jackrabbit.core.DefaultSecurityManager; -import org.apache.jackrabbit.core.security.AMContext; -import org.apache.jackrabbit.core.security.AccessManager; -import org.apache.jackrabbit.core.security.SecurityConstants; -import org.apache.jackrabbit.core.security.authorization.WorkspaceAccessManager; - -/** Integrates Spring Security and Jackrabbit Security users and roles. */ -public class ArgeoSecurityManager extends DefaultSecurityManager { - @Override - public AccessManager getAccessManager(Session session, AMContext amContext) - throws RepositoryException { - synchronized (getSystemSession()) { - return super.getAccessManager(session, amContext); - } - } - - @Override - public UserManager getUserManager(Session session) - throws RepositoryException { - synchronized (getSystemSession()) { - return super.getUserManager(session); - } - } - - /** - * Since this is called once when the session is created, we take the - * opportunity to make sure that Jackrabbit users and groups reflect Spring - * Security name and authorities. - */ - @Override - public String getUserID(Subject subject, String workspaceName) - throws RepositoryException { - Set userPrincipal = subject - .getPrincipals(X500Principal.class); - if (userPrincipal.isEmpty()) - return super.getUserID(subject, workspaceName); - if (userPrincipal.size() > 1) { - StringBuilder buf = new StringBuilder(); - for (X500Principal principal : userPrincipal) - buf.append(' ').append('\"').append(principal).append('\"'); - throw new RuntimeException("Multiple user principals:" + buf); - } - return userPrincipal.iterator().next().getName(); - // Authentication authentication = SecurityContextHolder.getContext() - // .getAuthentication(); - // if (authentication != null) - // return authentication.getName(); - // else - // return super.getUserID(subject, workspaceName); - } - - @Override - protected WorkspaceAccessManager createDefaultWorkspaceAccessManager() { - WorkspaceAccessManager wam = super - .createDefaultWorkspaceAccessManager(); - return new ArgeoWorkspaceAccessManagerImpl(wam); - } - - private class ArgeoWorkspaceAccessManagerImpl implements SecurityConstants, - WorkspaceAccessManager { - private final WorkspaceAccessManager wam; - - public ArgeoWorkspaceAccessManagerImpl(WorkspaceAccessManager wam) { - super(); - this.wam = wam; - } - - public void init(Session systemSession) throws RepositoryException { - wam.init(systemSession); - } - - public void close() throws RepositoryException { - } - - public boolean grants(Set principals, String workspaceName) - throws RepositoryException { - // TODO: implements finer access to workspaces - return true; - } - } - -} diff --git a/org.argeo.security.jackrabbit/src/org/argeo/security/jackrabbit/SystemJackrabbitLoginModule.java b/org.argeo.security.jackrabbit/src/org/argeo/security/jackrabbit/SystemJackrabbitLoginModule.java deleted file mode 100644 index 62f8fa02b..000000000 --- a/org.argeo.security.jackrabbit/src/org/argeo/security/jackrabbit/SystemJackrabbitLoginModule.java +++ /dev/null @@ -1,62 +0,0 @@ -package org.argeo.security.jackrabbit; - -import java.util.Map; -import java.util.Set; - -import javax.security.auth.Subject; -import javax.security.auth.callback.CallbackHandler; -import javax.security.auth.login.LoginException; -import javax.security.auth.spi.LoginModule; -import javax.security.auth.x500.X500Principal; - -import org.apache.jackrabbit.core.security.SecurityConstants; -import org.apache.jackrabbit.core.security.principal.AdminPrincipal; -import org.argeo.node.DataAdminPrincipal; - -public class SystemJackrabbitLoginModule implements LoginModule { - - private Subject subject; - - @Override - public void initialize(Subject subject, CallbackHandler callbackHandler, Map sharedState, - Map options) { - this.subject = subject; - } - - @Override - public boolean login() throws LoginException { - return true; - } - - @Override - public boolean commit() throws LoginException { - Set initPrincipal = subject.getPrincipals(DataAdminPrincipal.class); - if (!initPrincipal.isEmpty()) { - subject.getPrincipals().add(new AdminPrincipal(SecurityConstants.ADMIN_ID)); - return true; - } - - Set userPrincipal = subject.getPrincipals(X500Principal.class); - if (userPrincipal.isEmpty()) - throw new LoginException("Subject must be pre-authenticated"); - if (userPrincipal.size() > 1) - throw new LoginException("Multiple user principals " + userPrincipal); - - return true; - } - - @Override - public boolean abort() throws LoginException { - return true; - } - - @Override - public boolean logout() throws LoginException { - Set initPrincipal = subject.getPrincipals(DataAdminPrincipal.class); - if (!initPrincipal.isEmpty()) { - subject.getPrincipals(AdminPrincipal.class); - return true; - } - return true; - } -} diff --git a/org.argeo.security.ui.admin/.classpath b/org.argeo.security.ui.admin/.classpath deleted file mode 100644 index 457b11571..000000000 --- a/org.argeo.security.ui.admin/.classpath +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - diff --git a/org.argeo.security.ui.admin/.project b/org.argeo.security.ui.admin/.project deleted file mode 100644 index 9821dd947..000000000 --- a/org.argeo.security.ui.admin/.project +++ /dev/null @@ -1,28 +0,0 @@ - - - org.argeo.security.ui.admin - - - - - - org.eclipse.jdt.core.javabuilder - - - - - org.eclipse.pde.ManifestBuilder - - - - - org.eclipse.pde.SchemaBuilder - - - - - - org.eclipse.pde.PluginNature - org.eclipse.jdt.core.javanature - - diff --git a/org.argeo.security.ui.admin/META-INF/spring/commands.xml b/org.argeo.security.ui.admin/META-INF/spring/commands.xml deleted file mode 100644 index 7d3987644..000000000 --- a/org.argeo.security.ui.admin/META-INF/spring/commands.xml +++ /dev/null @@ -1,39 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/org.argeo.security.ui.admin/META-INF/spring/common.xml b/org.argeo.security.ui.admin/META-INF/spring/common.xml deleted file mode 100644 index f3b7ecb01..000000000 --- a/org.argeo.security.ui.admin/META-INF/spring/common.xml +++ /dev/null @@ -1,27 +0,0 @@ - - - - - - - - - - - - - - - - - osgibundle:security-admin.properties - - - - \ No newline at end of file diff --git a/org.argeo.security.ui.admin/META-INF/spring/osgi.xml b/org.argeo.security.ui.admin/META-INF/spring/osgi.xml deleted file mode 100644 index 02a63748f..000000000 --- a/org.argeo.security.ui.admin/META-INF/spring/osgi.xml +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - - - - \ No newline at end of file diff --git a/org.argeo.security.ui.admin/META-INF/spring/parts.xml b/org.argeo.security.ui.admin/META-INF/spring/parts.xml deleted file mode 100644 index cbc36e0c3..000000000 --- a/org.argeo.security.ui.admin/META-INF/spring/parts.xml +++ /dev/null @@ -1,29 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/org.argeo.security.ui.admin/bnd.bnd b/org.argeo.security.ui.admin/bnd.bnd deleted file mode 100644 index d5f00ceaf..000000000 --- a/org.argeo.security.ui.admin/bnd.bnd +++ /dev/null @@ -1,16 +0,0 @@ -Bundle-SymbolicName: org.argeo.security.ui.admin;singleton:=true -Bundle-Activator: org.argeo.security.ui.admin.SecurityAdminPlugin -Bundle-ActivationPolicy: lazy - -Require-Bundle: org.eclipse.core.runtime - -Import-Package: org.eclipse.core.runtime.jobs,\ -org.argeo.cms.auth,\ -org.argeo.eclipse.spring,\ -org.eclipse.jface.window,\ -org.eclipse.swt,\ -org.eclipse.swt.widgets,\ -org.eclipse.ui.services,\ -org.osgi.framework,\ -org.springframework.core,\ -* diff --git a/org.argeo.security.ui.admin/build.properties b/org.argeo.security.ui.admin/build.properties deleted file mode 100644 index 30f715358..000000000 --- a/org.argeo.security.ui.admin/build.properties +++ /dev/null @@ -1 +0,0 @@ -source.. = src/ diff --git a/org.argeo.security.ui.admin/icons/add.gif b/org.argeo.security.ui.admin/icons/add.gif deleted file mode 100644 index 252d7ebcb..000000000 Binary files a/org.argeo.security.ui.admin/icons/add.gif and /dev/null differ diff --git a/org.argeo.security.ui.admin/icons/batch.gif b/org.argeo.security.ui.admin/icons/batch.gif deleted file mode 100644 index b8ca14a8b..000000000 Binary files a/org.argeo.security.ui.admin/icons/batch.gif and /dev/null differ diff --git a/org.argeo.security.ui.admin/icons/begin.gif b/org.argeo.security.ui.admin/icons/begin.gif deleted file mode 100755 index feb8e94a7..000000000 Binary files a/org.argeo.security.ui.admin/icons/begin.gif and /dev/null differ diff --git a/org.argeo.security.ui.admin/icons/clear.gif b/org.argeo.security.ui.admin/icons/clear.gif deleted file mode 100644 index 6bc10f9d0..000000000 Binary files a/org.argeo.security.ui.admin/icons/clear.gif and /dev/null differ diff --git a/org.argeo.security.ui.admin/icons/commit.gif b/org.argeo.security.ui.admin/icons/commit.gif deleted file mode 100755 index 876f3eb16..000000000 Binary files a/org.argeo.security.ui.admin/icons/commit.gif and /dev/null differ diff --git a/org.argeo.security.ui.admin/icons/refresh.png b/org.argeo.security.ui.admin/icons/refresh.png deleted file mode 100644 index a3884fb48..000000000 Binary files a/org.argeo.security.ui.admin/icons/refresh.png and /dev/null differ diff --git a/org.argeo.security.ui.admin/icons/remove.gif b/org.argeo.security.ui.admin/icons/remove.gif deleted file mode 100644 index 0ae6decd0..000000000 Binary files a/org.argeo.security.ui.admin/icons/remove.gif and /dev/null differ diff --git a/org.argeo.security.ui.admin/icons/role.gif b/org.argeo.security.ui.admin/icons/role.gif deleted file mode 100644 index 274a850e4..000000000 Binary files a/org.argeo.security.ui.admin/icons/role.gif and /dev/null differ diff --git a/org.argeo.security.ui.admin/icons/rollback.gif b/org.argeo.security.ui.admin/icons/rollback.gif deleted file mode 100755 index c75399599..000000000 Binary files a/org.argeo.security.ui.admin/icons/rollback.gif and /dev/null differ diff --git a/org.argeo.security.ui.admin/icons/save.gif b/org.argeo.security.ui.admin/icons/save.gif deleted file mode 100644 index 654ad7b42..000000000 Binary files a/org.argeo.security.ui.admin/icons/save.gif and /dev/null differ diff --git a/org.argeo.security.ui.admin/icons/save_security.png b/org.argeo.security.ui.admin/icons/save_security.png deleted file mode 100644 index ca41dc92b..000000000 Binary files a/org.argeo.security.ui.admin/icons/save_security.png and /dev/null differ diff --git a/org.argeo.security.ui.admin/icons/save_security_disabled.png b/org.argeo.security.ui.admin/icons/save_security_disabled.png deleted file mode 100644 index fb7d08d9a..000000000 Binary files a/org.argeo.security.ui.admin/icons/save_security_disabled.png and /dev/null differ diff --git a/org.argeo.security.ui.admin/icons/security.gif b/org.argeo.security.ui.admin/icons/security.gif deleted file mode 100644 index 57fb95edc..000000000 Binary files a/org.argeo.security.ui.admin/icons/security.gif and /dev/null differ diff --git a/org.argeo.security.ui.admin/icons/sync.gif b/org.argeo.security.ui.admin/icons/sync.gif deleted file mode 100644 index b4fa052de..000000000 Binary files a/org.argeo.security.ui.admin/icons/sync.gif and /dev/null differ diff --git a/org.argeo.security.ui.admin/icons/user.gif b/org.argeo.security.ui.admin/icons/user.gif deleted file mode 100644 index 90a00147b..000000000 Binary files a/org.argeo.security.ui.admin/icons/user.gif and /dev/null differ diff --git a/org.argeo.security.ui.admin/icons/users.gif b/org.argeo.security.ui.admin/icons/users.gif deleted file mode 100644 index 2de7edd64..000000000 Binary files a/org.argeo.security.ui.admin/icons/users.gif and /dev/null differ diff --git a/org.argeo.security.ui.admin/plugin.xml b/org.argeo.security.ui.admin/plugin.xml deleted file mode 100644 index 2cf0ba210..000000000 --- a/org.argeo.security.ui.admin/plugin.xml +++ /dev/null @@ -1,230 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/org.argeo.security.ui.admin/pom.xml b/org.argeo.security.ui.admin/pom.xml deleted file mode 100644 index 3650ced6b..000000000 --- a/org.argeo.security.ui.admin/pom.xml +++ /dev/null @@ -1,46 +0,0 @@ - - - 4.0.0 - - org.argeo.commons - 2.1.46-SNAPSHOT - argeo-commons - .. - - org.argeo.security.ui.admin - Commons CMS Workbench Admin - jar - - - org.argeo.commons - org.argeo.cms - 2.1.46-SNAPSHOT - - - org.argeo.commons - org.argeo.util - 2.1.46-SNAPSHOT - - - org.argeo.commons - org.argeo.security.ui - 2.1.46-SNAPSHOT - - - org.argeo.commons - org.argeo.security.core - 2.1.46-SNAPSHOT - - - org.argeo.commons - org.argeo.eclipse.ui - 2.1.46-SNAPSHOT - - - org.argeo.commons - org.argeo.eclipse.ui.rap - 2.1.46-SNAPSHOT - provided - - - \ No newline at end of file diff --git a/org.argeo.security.ui.admin/security-admin.properties b/org.argeo.security.ui.admin/security-admin.properties deleted file mode 100644 index e69de29bb..000000000 diff --git a/org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/SecurityAdminImages.java b/org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/SecurityAdminImages.java deleted file mode 100644 index f15f8ec9a..000000000 --- a/org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/SecurityAdminImages.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Argeo Connect - Data management and communications - * Copyright (C) 2012 Argeo GmbH - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see - * - * Additional permission under GNU GPL version 3 section 7 - * - * If you modify this Program, or any covered work, by linking or combining it - * with software covered by the terms of the Eclipse Public License, the - * licensors of this Program grant you additional permission to convey the - * resulting work. Corresponding Source for a non-source form of such a - * combination shall include the source code for the parts of such software - * which are used as well as that of the covered work. - */ -package org.argeo.security.ui.admin; - -import org.eclipse.jface.resource.ImageDescriptor; -import org.eclipse.swt.graphics.Image; - -/** Shared icons that must be declared programmatically . */ -public class SecurityAdminImages { - private final static String PREFIX = "icons/"; - - public final static ImageDescriptor ICON_REMOVE_DESC = SecurityAdminPlugin - .getImageDescriptor(PREFIX + "remove.gif"); - public final static ImageDescriptor ICON_USER_DESC = SecurityAdminPlugin - .getImageDescriptor(PREFIX + "user.gif"); - - public final static Image ICON_USER = ICON_USER_DESC.createImage(); - public final static Image ICON_GROUP = SecurityAdminPlugin - .getImageDescriptor(PREFIX + "users.gif").createImage(); - public final static Image ICON_ROLE = SecurityAdminPlugin - .getImageDescriptor(PREFIX + "role.gif").createImage(); - -} diff --git a/org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/SecurityAdminPerspective.java b/org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/SecurityAdminPerspective.java deleted file mode 100644 index 2334827c9..000000000 --- a/org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/SecurityAdminPerspective.java +++ /dev/null @@ -1,36 +0,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. - */ -package org.argeo.security.ui.admin; - -import org.argeo.security.ui.admin.internal.parts.GroupsView; -import org.argeo.security.ui.admin.internal.parts.UsersView; -import org.eclipse.ui.IFolderLayout; -import org.eclipse.ui.IPageLayout; -import org.eclipse.ui.IPerspectiveFactory; - -/** Default perspective to manage users and groups */ -public class SecurityAdminPerspective implements IPerspectiveFactory { - public void createInitialLayout(IPageLayout layout) { - String editorArea = layout.getEditorArea(); - layout.setEditorAreaVisible(true); - layout.setFixed(false); - - IFolderLayout left = layout.createFolder("left", IPageLayout.LEFT, - 0.25f, editorArea); - left.addView(UsersView.ID); - left.addView(GroupsView.ID); - } -} diff --git a/org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/SecurityAdminPlugin.java b/org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/SecurityAdminPlugin.java deleted file mode 100644 index f9c0ad9af..000000000 --- a/org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/SecurityAdminPlugin.java +++ /dev/null @@ -1,54 +0,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. - */ -package org.argeo.security.ui.admin; - -import org.eclipse.jface.resource.ImageDescriptor; -import org.eclipse.ui.plugin.AbstractUIPlugin; -import org.osgi.framework.BundleContext; - -public class SecurityAdminPlugin extends AbstractUIPlugin { - public static final String PLUGIN_ID = "org.argeo.security.ui.admin"; //$NON-NLS-1$ - private static SecurityAdminPlugin plugin; - private static BundleContext bundleContext; - - public SecurityAdminPlugin() { - } - - public void start(BundleContext context) throws Exception { - super.start(context); - plugin = this; - bundleContext = context; - } - - public void stop(BundleContext context) throws Exception { - plugin = null; - bundleContext = null; - super.stop(context); - } - - public static SecurityAdminPlugin getDefault() { - return plugin; - } - - public static BundleContext getBundleContext() { - return bundleContext; - } - - public static ImageDescriptor getImageDescriptor(String path) { - return imageDescriptorFromPlugin(PLUGIN_ID, path); - } - -} diff --git a/org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/internal/PartStateChanged.java b/org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/internal/PartStateChanged.java deleted file mode 100644 index aacf6ef63..000000000 --- a/org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/internal/PartStateChanged.java +++ /dev/null @@ -1,58 +0,0 @@ -package org.argeo.security.ui.admin.internal; - -import org.argeo.cms.CmsException; -import org.eclipse.swt.widgets.Display; -import org.eclipse.ui.IPartListener; -import org.eclipse.ui.IStartup; -import org.eclipse.ui.IWorkbenchPage; -import org.eclipse.ui.IWorkbenchPart; -import org.eclipse.ui.PlatformUI; - -/** Manage transaction and part refresh while updating the security model */ -public class PartStateChanged implements IPartListener, IStartup { - // private final static Log log = LogFactory.getLog(PartStateChanged.class); - // private IContextActivation contextActivation; - - @Override - public void earlyStartup() { - Display.getDefault().asyncExec(new Runnable() { - public void run() { - try { - IWorkbenchPage iwp = PlatformUI.getWorkbench() - .getActiveWorkbenchWindow().getActivePage(); - if (iwp != null) - iwp.addPartListener(new PartStateChanged()); - } catch (Exception e) { - throw new CmsException( - "Error while registering the PartStateChangedListener", - e); - } - } - }); - } - - @Override - public void partActivated(IWorkbenchPart part) { - // Nothing to do - } - - @Override - public void partBroughtToTop(IWorkbenchPart part) { - // Nothing to do - } - - @Override - public void partClosed(IWorkbenchPart part) { - // Nothing to do - } - - @Override - public void partDeactivated(IWorkbenchPart part) { - // Nothing to do - } - - @Override - public void partOpened(IWorkbenchPart part) { - // Nothing to do - } -} \ No newline at end of file diff --git a/org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/internal/UiAdminUtils.java b/org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/internal/UiAdminUtils.java deleted file mode 100644 index 871877a0e..000000000 --- a/org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/internal/UiAdminUtils.java +++ /dev/null @@ -1,32 +0,0 @@ -package org.argeo.security.ui.admin.internal; - -import javax.transaction.UserTransaction; - -import org.argeo.cms.CmsException; -import org.argeo.security.ui.admin.internal.providers.UserTransactionProvider; -import org.eclipse.ui.IWorkbenchWindow; -import org.eclipse.ui.PlatformUI; -import org.eclipse.ui.services.ISourceProviderService; - -/** First effort to centralize back end methods used by the user admin UI */ -public class UiAdminUtils { - /* - * INTERNAL METHODS: Below methods are meant to stay here and are not part - * of a potential generic backend to manage the useradmin - */ - /** Easily notify the ActiveWindow that the transaction had a state change */ - public final static void notifyTransactionStateChange( - UserTransaction userTransaction) { - try { - IWorkbenchWindow aww = PlatformUI.getWorkbench() - .getActiveWorkbenchWindow(); - ISourceProviderService sourceProviderService = (ISourceProviderService) aww - .getService(ISourceProviderService.class); - UserTransactionProvider esp = (UserTransactionProvider) sourceProviderService - .getSourceProvider(UserTransactionProvider.TRANSACTION_STATE); - esp.fireTransactionStateChange(); - } catch (Exception e) { - throw new CmsException("Unable to begin transaction", e); - } - } -} \ No newline at end of file diff --git a/org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/internal/UiUserAdminListener.java b/org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/internal/UiUserAdminListener.java deleted file mode 100644 index 98c983683..000000000 --- a/org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/internal/UiUserAdminListener.java +++ /dev/null @@ -1,27 +0,0 @@ -package org.argeo.security.ui.admin.internal; - -import org.eclipse.swt.widgets.Display; -import org.osgi.service.useradmin.UserAdminEvent; -import org.osgi.service.useradmin.UserAdminListener; - -/** Convenience class to insure the call to refresh is done in the UI thread */ -public abstract class UiUserAdminListener implements UserAdminListener { - - private final Display display; - - public UiUserAdminListener(Display display) { - this.display = display; - } - - @Override - public void roleChanged(final UserAdminEvent event) { - display.asyncExec(new Runnable() { - @Override - public void run() { - roleChangedToUiThread(event); - } - }); - } - - public abstract void roleChangedToUiThread(UserAdminEvent event); -} diff --git a/org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/internal/UserAdminWrapper.java b/org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/internal/UserAdminWrapper.java deleted file mode 100644 index d443a3d47..000000000 --- a/org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/internal/UserAdminWrapper.java +++ /dev/null @@ -1,77 +0,0 @@ -package org.argeo.security.ui.admin.internal; - -import java.util.ArrayList; -import java.util.List; - -import javax.transaction.Status; -import javax.transaction.UserTransaction; - -import org.argeo.cms.CmsException; -import org.osgi.service.useradmin.UserAdminEvent; -import org.osgi.service.useradmin.UserAdminListener; - -/** Centralise interaction with the UserAdmin in this bundle */ -public class UserAdminWrapper extends - org.argeo.cms.util.useradmin.UserAdminWrapper { - - // First effort to simplify UX while managing users and groups - public final static boolean COMMIT_ON_SAVE = true; - - // Registered listeners - List listeners = new ArrayList(); - - /** - * Starts a transaction if necessary. Should always been called together - * with {@link UserAdminWrapper#commitOrNotifyTransactionStateChange()} once - * the security model changes have been performed. - */ - public UserTransaction beginTransactionIfNeeded() { - try { - UserTransaction userTransaction = getUserTransaction(); - if (userTransaction.getStatus() == Status.STATUS_NO_TRANSACTION) { - userTransaction.begin(); - // UiAdminUtils.notifyTransactionStateChange(userTransaction); - } - return userTransaction; - } catch (Exception e) { - throw new CmsException("Unable to begin transaction", e); - } - } - - /** - * Depending on the current application configuration, it will either commit - * the current transaction or throw a notification that the transaction - * state has changed (In the later case, it must be called from the UI - * thread). - */ - public void commitOrNotifyTransactionStateChange() { - try { - UserTransaction userTransaction = getUserTransaction(); - if (userTransaction.getStatus() == Status.STATUS_NO_TRANSACTION) - return; - - if (UserAdminWrapper.COMMIT_ON_SAVE) - userTransaction.commit(); - else - UiAdminUtils.notifyTransactionStateChange(userTransaction); - } catch (Exception e) { - throw new CmsException("Unable to clean transaction", e); - } - } - - // TODO implement safer mechanism - public void addListener(UserAdminListener userAdminListener) { - if (!listeners.contains(userAdminListener)) - listeners.add(userAdminListener); - } - - public void removeListener(UserAdminListener userAdminListener) { - if (listeners.contains(userAdminListener)) - listeners.remove(userAdminListener); - } - - public void notifyListeners(UserAdminEvent event) { - for (UserAdminListener listener : listeners) - listener.roleChanged(event); - } -} \ No newline at end of file diff --git a/org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/internal/commands/DeleteGroups.java b/org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/internal/commands/DeleteGroups.java deleted file mode 100644 index df5d430ab..000000000 --- a/org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/internal/commands/DeleteGroups.java +++ /dev/null @@ -1,100 +0,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. - */ -package org.argeo.security.ui.admin.internal.commands; - -import java.util.ArrayList; -import java.util.Iterator; -import java.util.List; - -import org.argeo.cms.util.useradmin.UserAdminUtils; -import org.argeo.security.ui.admin.SecurityAdminPlugin; -import org.argeo.security.ui.admin.internal.UserAdminWrapper; -import org.argeo.security.ui.admin.internal.parts.UserEditorInput; -import org.eclipse.core.commands.AbstractHandler; -import org.eclipse.core.commands.ExecutionEvent; -import org.eclipse.core.commands.ExecutionException; -import org.eclipse.jface.dialogs.MessageDialog; -import org.eclipse.jface.viewers.ISelection; -import org.eclipse.jface.viewers.IStructuredSelection; -import org.eclipse.ui.IEditorPart; -import org.eclipse.ui.IWorkbenchPage; -import org.eclipse.ui.handlers.HandlerUtil; -import org.osgi.service.useradmin.Group; -import org.osgi.service.useradmin.UserAdmin; -import org.osgi.service.useradmin.UserAdminEvent; - -/** Delete the selected groups */ -public class DeleteGroups extends AbstractHandler { - public final static String ID = SecurityAdminPlugin.PLUGIN_ID - + ".deleteGroups"; - - /* DEPENDENCY INJECTION */ - private UserAdminWrapper userAdminWrapper; - - @SuppressWarnings("unchecked") - public Object execute(ExecutionEvent event) throws ExecutionException { - ISelection selection = HandlerUtil.getCurrentSelection(event); - if (selection.isEmpty()) - return null; - - List groups = new ArrayList(); - Iterator it = ((IStructuredSelection) selection).iterator(); - StringBuilder builder = new StringBuilder(); - while (it.hasNext()) { - Group currGroup = it.next(); - String groupName = UserAdminUtils.getUsername(currGroup); - // TODO add checks - builder.append(groupName).append("; "); - groups.add(currGroup); - } - - if (!MessageDialog.openQuestion(HandlerUtil.getActiveShell(event), - "Delete Groups", - "Are you sure that you " + "want to delete these groups?\n" - + builder.substring(0, builder.length() - 2))) - return null; - - userAdminWrapper.beginTransactionIfNeeded(); - UserAdmin userAdmin = userAdminWrapper.getUserAdmin(); - IWorkbenchPage iwp = HandlerUtil.getActiveWorkbenchWindow(event) - .getActivePage(); - for (Group group : groups) { - String groupName = group.getName(); - // TODO find a way to close the editor cleanly if opened. Cannot be - // done through the UserAdminListeners, it causes a - // java.util.ConcurrentModificationException because disposing the - // editor unregisters and disposes the listener - IEditorPart part = iwp.findEditor(new UserEditorInput(groupName)); - if (part != null) - iwp.closeEditor(part, false); - userAdmin.removeRole(groupName); - } - userAdminWrapper.commitOrNotifyTransactionStateChange(); - - // Update the view - for (Group group : groups) { - userAdminWrapper.notifyListeners(new UserAdminEvent(null, - UserAdminEvent.ROLE_REMOVED, group)); - } - - return null; - } - - /* DEPENDENCY INJECTION */ - public void setUserAdminWrapper(UserAdminWrapper userAdminWrapper) { - this.userAdminWrapper = userAdminWrapper; - } -} \ No newline at end of file diff --git a/org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/internal/commands/DeleteUsers.java b/org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/internal/commands/DeleteUsers.java deleted file mode 100644 index e583bef53..000000000 --- a/org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/internal/commands/DeleteUsers.java +++ /dev/null @@ -1,106 +0,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. - */ -package org.argeo.security.ui.admin.internal.commands; - -import java.util.ArrayList; -import java.util.Iterator; -import java.util.List; - -import org.argeo.cms.util.useradmin.UserAdminUtils; -import org.argeo.security.ui.admin.SecurityAdminPlugin; -import org.argeo.security.ui.admin.internal.UserAdminWrapper; -import org.argeo.security.ui.admin.internal.parts.UserEditorInput; -import org.eclipse.core.commands.AbstractHandler; -import org.eclipse.core.commands.ExecutionEvent; -import org.eclipse.core.commands.ExecutionException; -import org.eclipse.jface.dialogs.MessageDialog; -import org.eclipse.jface.viewers.ISelection; -import org.eclipse.jface.viewers.IStructuredSelection; -import org.eclipse.ui.IEditorPart; -import org.eclipse.ui.IWorkbenchPage; -import org.eclipse.ui.handlers.HandlerUtil; -import org.osgi.service.useradmin.User; -import org.osgi.service.useradmin.UserAdmin; -import org.osgi.service.useradmin.UserAdminEvent; - -/** Delete the selected users */ -public class DeleteUsers extends AbstractHandler { - public final static String ID = SecurityAdminPlugin.PLUGIN_ID - + ".deleteUsers"; - - /* DEPENDENCY INJECTION */ - private UserAdminWrapper userAdminWrapper; - - @SuppressWarnings("unchecked") - public Object execute(ExecutionEvent event) throws ExecutionException { - ISelection selection = HandlerUtil.getCurrentSelection(event); - if (selection.isEmpty()) - return null; - - Iterator it = ((IStructuredSelection) selection).iterator(); - List users = new ArrayList(); - StringBuilder builder = new StringBuilder(); - - while (it.hasNext()) { - User currUser = it.next(); - String userName = UserAdminUtils.getUsername(currUser); - if (UserAdminUtils.isCurrentUser(currUser)) { - MessageDialog.openError(HandlerUtil.getActiveShell(event), - "Deletion forbidden", - "You cannot delete your own user this way."); - return null; - } - builder.append(userName).append("; "); - users.add(currUser); - } - - if (!MessageDialog.openQuestion( - HandlerUtil.getActiveShell(event), - "Delete Users", - "Are you sure that you want to delete these users?\n" - + builder.substring(0, builder.length() - 2))) - return null; - - userAdminWrapper.beginTransactionIfNeeded(); - UserAdmin userAdmin = userAdminWrapper.getUserAdmin(); - IWorkbenchPage iwp = HandlerUtil.getActiveWorkbenchWindow(event) - .getActivePage(); - - for (User user : users) { - String userName = user.getName(); - // TODO find a way to close the editor cleanly if opened. Cannot be - // done through the UserAdminListeners, it causes a - // java.util.ConcurrentModificationException because disposing the - // editor unregisters and disposes the listener - IEditorPart part = iwp.findEditor(new UserEditorInput(userName)); - if (part != null) - iwp.closeEditor(part, false); - userAdmin.removeRole(userName); - } - userAdminWrapper.commitOrNotifyTransactionStateChange(); - - for (User user : users) { - userAdminWrapper.notifyListeners(new UserAdminEvent(null, - UserAdminEvent.ROLE_REMOVED, user)); - } - return null; - } - - /* DEPENDENCY INJECTION */ - public void setUserAdminWrapper(UserAdminWrapper userAdminWrapper) { - this.userAdminWrapper = userAdminWrapper; - } -} \ No newline at end of file diff --git a/org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/internal/commands/ForceRefresh.java b/org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/internal/commands/ForceRefresh.java deleted file mode 100644 index 231c8718a..000000000 --- a/org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/internal/commands/ForceRefresh.java +++ /dev/null @@ -1,43 +0,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. - */ -package org.argeo.security.ui.admin.internal.commands; - -import org.argeo.security.ui.admin.internal.parts.GroupsView; -import org.argeo.security.ui.admin.internal.parts.UsersView; -import org.eclipse.core.commands.AbstractHandler; -import org.eclipse.core.commands.ExecutionEvent; -import org.eclipse.core.commands.ExecutionException; -import org.eclipse.ui.IWorkbenchPage; -import org.eclipse.ui.IWorkbenchPart; -import org.eclipse.ui.IWorkbenchWindow; -import org.eclipse.ui.handlers.HandlerUtil; - -/** Retrieve the active view or editor and call forceRefresh method if defined */ -public class ForceRefresh extends AbstractHandler { - - public Object execute(ExecutionEvent event) throws ExecutionException { - IWorkbenchWindow iww = HandlerUtil.getActiveWorkbenchWindow(event); - if (iww == null) - return null; - IWorkbenchPage activePage = iww.getActivePage(); - IWorkbenchPart part = activePage.getActivePart(); - if (part instanceof UsersView) - ((UsersView) part).refresh(); - else if (part instanceof GroupsView) - ((GroupsView) part).refresh(); - return null; - } -} \ No newline at end of file diff --git a/org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/internal/commands/NewGroup.java b/org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/internal/commands/NewGroup.java deleted file mode 100644 index 75b9b0b33..000000000 --- a/org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/internal/commands/NewGroup.java +++ /dev/null @@ -1,240 +0,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. - */ -package org.argeo.security.ui.admin.internal.commands; - -import java.util.Dictionary; -import java.util.Map; - -import org.argeo.cms.CmsException; -import org.argeo.eclipse.ui.EclipseUiUtils; -import org.argeo.eclipse.ui.dialogs.ErrorFeedback; -import org.argeo.jcr.ArgeoNames; -import org.argeo.osgi.useradmin.LdifName; -import org.argeo.osgi.useradmin.UserAdminConf; -import org.argeo.security.ui.admin.SecurityAdminPlugin; -import org.argeo.security.ui.admin.internal.UserAdminWrapper; -import org.eclipse.core.commands.AbstractHandler; -import org.eclipse.core.commands.ExecutionEvent; -import org.eclipse.core.commands.ExecutionException; -import org.eclipse.jface.wizard.Wizard; -import org.eclipse.jface.wizard.WizardDialog; -import org.eclipse.jface.wizard.WizardPage; -import org.eclipse.swt.SWT; -import org.eclipse.swt.events.FocusEvent; -import org.eclipse.swt.events.FocusListener; -import org.eclipse.swt.layout.GridData; -import org.eclipse.swt.layout.GridLayout; -import org.eclipse.swt.widgets.Combo; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Label; -import org.eclipse.swt.widgets.Text; -import org.eclipse.ui.handlers.HandlerUtil; -import org.osgi.service.useradmin.Group; -import org.osgi.service.useradmin.Role; -import org.osgi.service.useradmin.UserAdminEvent; - -/** Create a new group */ -public class NewGroup extends AbstractHandler { - public final static String ID = SecurityAdminPlugin.PLUGIN_ID + ".newGroup"; - - /* DEPENDENCY INJECTION */ - private UserAdminWrapper userAdminWrapper; - - public Object execute(ExecutionEvent event) throws ExecutionException { - NewGroupWizard newGroupWizard = new NewGroupWizard(); - newGroupWizard.setWindowTitle("Group creation"); - WizardDialog dialog = new WizardDialog( - HandlerUtil.getActiveShell(event), newGroupWizard); - dialog.open(); - return null; - } - - private class NewGroupWizard extends Wizard { - - // Pages - private MainGroupInfoWizardPage mainGroupInfo; - - // UI fields - private Text dNameTxt, commonNameTxt, descriptionTxt; - private Combo baseDnCmb; - - public NewGroupWizard() { - } - - @Override - public void addPages() { - mainGroupInfo = new MainGroupInfoWizardPage(); - addPage(mainGroupInfo); - } - - @SuppressWarnings({ "rawtypes", "unchecked" }) - @Override - public boolean performFinish() { - if (!canFinish()) - return false; - String commonName = commonNameTxt.getText(); - try { - userAdminWrapper.beginTransactionIfNeeded(); - String dn = getDn(commonName); - Group group = (Group) userAdminWrapper.getUserAdmin() - .createRole(dn, Role.GROUP); - Dictionary props = group.getProperties(); - String descStr = descriptionTxt.getText(); - if (EclipseUiUtils.notEmpty(descStr)) - props.put(LdifName.description.name(), descStr); - userAdminWrapper.commitOrNotifyTransactionStateChange(); - userAdminWrapper.notifyListeners(new UserAdminEvent(null, - UserAdminEvent.ROLE_CREATED, group)); - return true; - } catch (Exception e) { - ErrorFeedback.show("Cannot create new group " + commonName, e); - return false; - } - } - - private class MainGroupInfoWizardPage extends WizardPage implements - FocusListener, ArgeoNames { - private static final long serialVersionUID = -3150193365151601807L; - - public MainGroupInfoWizardPage() { - super("Main"); - setTitle("General information"); - setMessage("Please choose a domain, provide a common name " - + "and a free description"); - } - - @Override - public void createControl(Composite parent) { - Composite bodyCmp = new Composite(parent, SWT.NONE); - setControl(bodyCmp); - bodyCmp.setLayout(new GridLayout(2, false)); - - dNameTxt = EclipseUiUtils.createGridLT(bodyCmp, - "Distinguished name"); - dNameTxt.setEnabled(false); - - baseDnCmb = createGridLC(bodyCmp, "Base DN"); - // Initialise before adding the listener to avoid NPE - initialiseDnCmb(baseDnCmb); - baseDnCmb.addFocusListener(this); - - commonNameTxt = EclipseUiUtils.createGridLT(bodyCmp, - "Common name"); - commonNameTxt.addFocusListener(this); - - Label descLbl = new Label(bodyCmp, SWT.LEAD); - descLbl.setText("Description"); - descLbl.setLayoutData(new GridData(SWT.RIGHT, SWT.TOP, false, - false)); - descriptionTxt = new Text(bodyCmp, SWT.LEAD | SWT.MULTI - | SWT.WRAP | SWT.BORDER); - descriptionTxt.setLayoutData(EclipseUiUtils.fillAll()); - descriptionTxt.addFocusListener(this); - - // Initialize buttons - setPageComplete(false); - getContainer().updateButtons(); - } - - @Override - public void focusLost(FocusEvent event) { - String name = commonNameTxt.getText(); - if (EclipseUiUtils.isEmpty(name)) - dNameTxt.setText(""); - else - dNameTxt.setText(getDn(name)); - - String message = checkComplete(); - if (message != null) { - setMessage(message, WizardPage.ERROR); - setPageComplete(false); - } else { - setMessage("Complete", WizardPage.INFORMATION); - setPageComplete(true); - } - getContainer().updateButtons(); - } - - @Override - public void focusGained(FocusEvent event) { - } - - /** @return the error message or null if complete */ - protected String checkComplete() { - String name = commonNameTxt.getText(); - - if (name.trim().equals("")) - return "Common name must not be empty"; - Role role = userAdminWrapper.getUserAdmin() - .getRole(getDn(name)); - if (role != null) - return "Group " + name + " already exists"; - return null; - } - - @Override - public void setVisible(boolean visible) { - super.setVisible(visible); - if (visible) - if (baseDnCmb.getSelectionIndex() == -1) - baseDnCmb.setFocus(); - else - commonNameTxt.setFocus(); - } - } - - private Map getDns() { - return userAdminWrapper.getKnownBaseDns(true); - } - - private String getDn(String cn) { - Map dns = getDns(); - String bdn = baseDnCmb.getText(); - if (EclipseUiUtils.notEmpty(bdn)) { - Dictionary props = UserAdminConf.uriAsProperties(dns - .get(bdn)); - String dn = LdifName.cn.name() + "=" + cn + "," - + UserAdminConf.groupBase.getValue(props) + "," + bdn; - return dn; - } - return null; - } - - private void initialiseDnCmb(Combo combo) { - Map dns = userAdminWrapper.getKnownBaseDns(true); - if (dns.isEmpty()) - throw new CmsException( - "No writable base dn found. Cannot create group"); - combo.setItems(dns.keySet().toArray(new String[0])); - if (dns.size() == 1) - combo.select(0); - } - } - - private Combo createGridLC(Composite parent, String label) { - Label lbl = new Label(parent, SWT.LEAD); - lbl.setText(label); - lbl.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false)); - Combo combo = new Combo(parent, SWT.LEAD | SWT.BORDER | SWT.READ_ONLY); - combo.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false)); - return combo; - } - - /* DEPENDENCY INJECTION */ - public void setUserAdminWrapper(UserAdminWrapper userAdminWrapper) { - this.userAdminWrapper = userAdminWrapper; - } -} \ No newline at end of file diff --git a/org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/internal/commands/NewUser.java b/org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/internal/commands/NewUser.java deleted file mode 100644 index c04c83562..000000000 --- a/org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/internal/commands/NewUser.java +++ /dev/null @@ -1,328 +0,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. - */ -package org.argeo.security.ui.admin.internal.commands; - -import java.util.Dictionary; -import java.util.List; -import java.util.Map; - -import javax.naming.InvalidNameException; -import javax.naming.ldap.LdapName; -import javax.naming.ldap.Rdn; - -import org.argeo.cms.CmsException; -import org.argeo.cms.util.useradmin.UserAdminUtils; -import org.argeo.eclipse.ui.EclipseUiUtils; -import org.argeo.eclipse.ui.dialogs.ErrorFeedback; -import org.argeo.jcr.ArgeoNames; -import org.argeo.osgi.useradmin.LdifName; -import org.argeo.osgi.useradmin.UserAdminConf; -import org.argeo.security.ui.admin.SecurityAdminPlugin; -import org.argeo.security.ui.admin.internal.UserAdminWrapper; -import org.eclipse.core.commands.AbstractHandler; -import org.eclipse.core.commands.ExecutionEvent; -import org.eclipse.core.commands.ExecutionException; -import org.eclipse.jface.wizard.Wizard; -import org.eclipse.jface.wizard.WizardDialog; -import org.eclipse.jface.wizard.WizardPage; -import org.eclipse.swt.SWT; -import org.eclipse.swt.events.ModifyEvent; -import org.eclipse.swt.events.ModifyListener; -import org.eclipse.swt.layout.GridData; -import org.eclipse.swt.layout.GridLayout; -import org.eclipse.swt.widgets.Combo; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Label; -import org.eclipse.swt.widgets.Text; -import org.eclipse.ui.handlers.HandlerUtil; -import org.osgi.service.useradmin.Role; -import org.osgi.service.useradmin.User; -import org.osgi.service.useradmin.UserAdminEvent; - -/** Open a wizard that enables creation of a new user. */ -public class NewUser extends AbstractHandler { - /** - * Email addresses must match this regexp pattern ({@value #EMAIL_PATTERN}. - * Thanks to this tip. - */ - public final static String EMAIL_PATTERN = "^[_A-Za-z0-9-]+(\\.[_A-Za-z0-9-]+)*@[A-Za-z0-9-]+(\\.[A-Za-z0-9]+)*(\\.[A-Za-z]{2,})$"; - // private final static Log log = LogFactory.getLog(NewUser.class); - public final static String ID = SecurityAdminPlugin.PLUGIN_ID + ".newUser"; - - /* DEPENDENCY INJECTION */ - private UserAdminWrapper userAdminWrapper; - - public Object execute(ExecutionEvent event) throws ExecutionException { - NewUserWizard newUserWizard = new NewUserWizard(); - newUserWizard.setWindowTitle("User creation"); - WizardDialog dialog = new WizardDialog( - HandlerUtil.getActiveShell(event), newUserWizard); - dialog.open(); - return null; - } - - private class NewUserWizard extends Wizard { - - // pages - private MainUserInfoWizardPage mainUserInfo; - - // End user fields - private Text dNameTxt, usernameTxt, firstNameTxt, lastNameTxt, - primaryMailTxt, pwd1Txt, pwd2Txt; - private Combo baseDnCmb; - - public NewUserWizard() { - - } - - @Override - public void addPages() { - mainUserInfo = new MainUserInfoWizardPage(); - addPage(mainUserInfo); - String message = "Default wizard that also eases user creation tests:\n " - + "Mail and last name are automatically " - + "generated form the uid. Password are defauted to 'demo'."; - mainUserInfo.setMessage(message, WizardPage.WARNING); - } - - @SuppressWarnings({ "rawtypes", "unchecked" }) - @Override - public boolean performFinish() { - if (!canFinish()) - return false; - String username = mainUserInfo.getUsername(); - userAdminWrapper.beginTransactionIfNeeded(); - try { - User user = (User) userAdminWrapper.getUserAdmin().createRole( - getDn(username), Role.USER); - - Dictionary props = user.getProperties(); - - String lastNameStr = lastNameTxt.getText(); - if (EclipseUiUtils.notEmpty(lastNameStr)) - props.put(LdifName.sn.name(), lastNameStr); - - String firstNameStr = firstNameTxt.getText(); - if (EclipseUiUtils.notEmpty(firstNameStr)) - props.put(LdifName.givenName.name(), firstNameStr); - - String cn = UserAdminUtils.buildDefaultCn(firstNameStr, - lastNameStr); - if (EclipseUiUtils.notEmpty(cn)) - props.put(LdifName.cn.name(), cn); - - String mailStr = primaryMailTxt.getText(); - if (EclipseUiUtils.notEmpty(mailStr)) - props.put(LdifName.mail.name(), mailStr); - - char[] password = mainUserInfo.getPassword(); - user.getCredentials().put(null, password); - userAdminWrapper.commitOrNotifyTransactionStateChange(); - userAdminWrapper.notifyListeners(new UserAdminEvent(null, - UserAdminEvent.ROLE_CREATED, user)); - return true; - } catch (Exception e) { - ErrorFeedback.show("Cannot create new user " + username, e); - return false; - } - } - - private class MainUserInfoWizardPage extends WizardPage implements - ModifyListener, ArgeoNames { - private static final long serialVersionUID = -3150193365151601807L; - - public MainUserInfoWizardPage() { - super("Main"); - setTitle("Required Information"); - } - - @Override - public void createControl(Composite parent) { - Composite composite = new Composite(parent, SWT.NONE); - composite.setLayout(new GridLayout(2, false)); - dNameTxt = EclipseUiUtils.createGridLT(composite, - "Distinguished name", this); - dNameTxt.setEnabled(false); - - baseDnCmb = createGridLC(composite, "Base DN"); - initialiseDnCmb(baseDnCmb); - baseDnCmb.addModifyListener(this); - baseDnCmb.addModifyListener(new ModifyListener() { - private static final long serialVersionUID = -1435351236582736843L; - - @Override - public void modifyText(ModifyEvent event) { - String name = usernameTxt.getText(); - dNameTxt.setText(getDn(name)); - } - }); - - usernameTxt = EclipseUiUtils.createGridLT(composite, - "Local ID", this); - usernameTxt.addModifyListener(new ModifyListener() { - private static final long serialVersionUID = -1435351236582736843L; - - @Override - public void modifyText(ModifyEvent event) { - String name = usernameTxt.getText(); - if (name.trim().equals("")) { - dNameTxt.setText(""); - lastNameTxt.setText(""); - primaryMailTxt.setText(""); - pwd1Txt.setText(""); - pwd2Txt.setText(""); - } else { - dNameTxt.setText(getDn(name)); - lastNameTxt.setText(name.toUpperCase()); - primaryMailTxt.setText(getMail(name)); - pwd1Txt.setText("demo"); - pwd2Txt.setText("demo"); - } - } - }); - - primaryMailTxt = EclipseUiUtils.createGridLT(composite, - "Email", this); - firstNameTxt = EclipseUiUtils.createGridLT(composite, - "First name", this); - lastNameTxt = EclipseUiUtils.createGridLT(composite, - "Last name", this); - pwd1Txt = EclipseUiUtils.createGridLP(composite, "Password", - this); - pwd2Txt = EclipseUiUtils.createGridLP(composite, - "Repeat password", this); - setControl(composite); - - // Initialize buttons - setPageComplete(false); - getContainer().updateButtons(); - } - - @Override - public void modifyText(ModifyEvent event) { - String message = checkComplete(); - if (message != null) { - setMessage(message, WizardPage.ERROR); - setPageComplete(false); - } else { - setMessage("Complete", WizardPage.INFORMATION); - setPageComplete(true); - } - getContainer().updateButtons(); - } - - /** @return error message or null if complete */ - protected String checkComplete() { - String name = usernameTxt.getText(); - - if (name.trim().equals("")) - return "User name must not be empty"; - Role role = userAdminWrapper.getUserAdmin() - .getRole(getDn(name)); - if (role != null) - return "User " + name + " already exists"; - if (!primaryMailTxt.getText().matches(EMAIL_PATTERN)) - return "Not a valid email address"; - if (lastNameTxt.getText().trim().equals("")) - return "Specify a last name"; - if (pwd1Txt.getText().trim().equals("")) - return "Specify a password"; - if (pwd2Txt.getText().trim().equals("")) - return "Repeat the password"; - if (!pwd2Txt.getText().equals(pwd1Txt.getText())) - return "Passwords are different"; - return null; - } - - @Override - public void setVisible(boolean visible) { - super.setVisible(visible); - if (visible) - if (baseDnCmb.getSelectionIndex() == -1) - baseDnCmb.setFocus(); - else - usernameTxt.setFocus(); - } - - public String getUsername() { - return usernameTxt.getText(); - } - - public char[] getPassword() { - return pwd1Txt.getTextChars(); - } - - } - - private Map getDns() { - return userAdminWrapper.getKnownBaseDns(true); - } - - private String getDn(String uid) { - Map dns = getDns(); - String bdn = baseDnCmb.getText(); - if (EclipseUiUtils.notEmpty(bdn)) { - Dictionary props = UserAdminConf.uriAsProperties(dns - .get(bdn)); - String dn = LdifName.uid.name() + "=" + uid + "," - + UserAdminConf.userBase.getValue(props) + "," + bdn; - return dn; - } - return null; - } - - private void initialiseDnCmb(Combo combo) { - Map dns = userAdminWrapper.getKnownBaseDns(true); - if (dns.isEmpty()) - throw new CmsException( - "No writable base dn found. Cannot create user"); - combo.setItems(dns.keySet().toArray(new String[0])); - if (dns.size() == 1) - combo.select(0); - } - - private String getMail(String username) { - if (baseDnCmb.getSelectionIndex() == -1) - return null; - String baseDn = baseDnCmb.getText(); - try { - LdapName name = new LdapName(baseDn); - List rdns = name.getRdns(); - return username + "@" + (String) rdns.get(1).getValue() + '.' - + (String) rdns.get(0).getValue(); - } catch (InvalidNameException e) { - throw new CmsException("Unable to generate mail for " - + username + " with base dn " + baseDn, e); - } - } - } - - private Combo createGridLC(Composite parent, String label) { - Label lbl = new Label(parent, SWT.LEAD); - lbl.setText(label); - lbl.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false)); - Combo combo = new Combo(parent, SWT.LEAD | SWT.BORDER | SWT.READ_ONLY); - combo.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false)); - return combo; - } - - /* DEPENDENCY INJECTION */ - public void setUserAdminWrapper(UserAdminWrapper userAdminWrapper) { - this.userAdminWrapper = userAdminWrapper; - } -} \ No newline at end of file diff --git a/org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/internal/commands/SaveArgeoUser.java b/org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/internal/commands/SaveArgeoUser.java deleted file mode 100644 index 61d8a7daf..000000000 --- a/org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/internal/commands/SaveArgeoUser.java +++ /dev/null @@ -1,47 +0,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. - */ -package org.argeo.security.ui.admin.internal.commands; - -import org.argeo.security.ui.admin.SecurityAdminPlugin; -import org.eclipse.core.commands.AbstractHandler; -import org.eclipse.core.commands.ExecutionEvent; -import org.eclipse.core.commands.ExecutionException; -import org.eclipse.jface.dialogs.MessageDialog; -import org.eclipse.swt.widgets.Display; -import org.eclipse.ui.IEditorPart; -import org.eclipse.ui.IWorkbenchPart; -import org.eclipse.ui.handlers.HandlerUtil; - -/** Save the currently edited Argeo user. */ -public class SaveArgeoUser extends AbstractHandler { - public final static String ID = SecurityAdminPlugin.PLUGIN_ID - + ".saveArgeoUser"; - - public Object execute(ExecutionEvent event) throws ExecutionException { - try { - IWorkbenchPart iwp = HandlerUtil.getActiveWorkbenchWindow(event) - .getActivePage().getActivePart(); - if (!(iwp instanceof IEditorPart)) - return null; - IEditorPart editor = (IEditorPart) iwp; - editor.doSave(null); - } catch (Exception e) { - MessageDialog.openError(Display.getDefault().getActiveShell(), - "Error", "Cannot save user: " + e.getMessage()); - } - return null; - } -} \ No newline at end of file diff --git a/org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/internal/commands/UserBatchUpdate.java b/org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/internal/commands/UserBatchUpdate.java deleted file mode 100644 index c02f5c220..000000000 --- a/org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/internal/commands/UserBatchUpdate.java +++ /dev/null @@ -1,44 +0,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. - */ -package org.argeo.security.ui.admin.internal.commands; - -import org.argeo.security.ui.admin.internal.UserAdminWrapper; -import org.argeo.security.ui.admin.internal.parts.UserBatchUpdateWizard; -import org.eclipse.core.commands.AbstractHandler; -import org.eclipse.core.commands.ExecutionEvent; -import org.eclipse.core.commands.ExecutionException; -import org.eclipse.jface.wizard.WizardDialog; -import org.eclipse.ui.handlers.HandlerUtil; - -/** Launch a wizard to perform batch process on users */ -public class UserBatchUpdate extends AbstractHandler { - - /* DEPENDENCY INJECTION */ - private UserAdminWrapper uaWrapper; - - public Object execute(ExecutionEvent event) throws ExecutionException { - UserBatchUpdateWizard wizard = new UserBatchUpdateWizard(uaWrapper); - wizard.setWindowTitle("User batch processing"); - WizardDialog dialog = new WizardDialog( - HandlerUtil.getActiveShell(event), wizard); - dialog.open(); - return null; - } - - public void setUserAdminWrapper(UserAdminWrapper userAdminWrapper) { - this.uaWrapper = userAdminWrapper; - } -} \ No newline at end of file diff --git a/org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/internal/commands/UserTransactionHandler.java b/org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/internal/commands/UserTransactionHandler.java deleted file mode 100644 index 236584ca3..000000000 --- a/org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/internal/commands/UserTransactionHandler.java +++ /dev/null @@ -1,93 +0,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. - */ -package org.argeo.security.ui.admin.internal.commands; - -import javax.transaction.Status; -import javax.transaction.UserTransaction; - -import org.argeo.cms.CmsException; -import org.argeo.security.ui.admin.SecurityAdminPlugin; -import org.argeo.security.ui.admin.internal.UiAdminUtils; -import org.argeo.security.ui.admin.internal.UserAdminWrapper; -import org.eclipse.core.commands.AbstractHandler; -import org.eclipse.core.commands.ExecutionEvent; -import org.eclipse.core.commands.ExecutionException; -import org.osgi.service.useradmin.UserAdminEvent; - -/** Manage the transaction that is bound to the current perspective */ -public class UserTransactionHandler extends AbstractHandler { - public final static String ID = SecurityAdminPlugin.PLUGIN_ID - + ".userTransactionHandler"; - - public final static String PARAM_COMMAND_ID = "param.commandId"; - - public final static String TRANSACTION_BEGIN = "transaction.begin"; - public final static String TRANSACTION_COMMIT = "transaction.commit"; - public final static String TRANSACTION_ROLLBACK = "transaction.rollback"; - - /* DEPENDENCY INJECTION */ - private UserAdminWrapper userAdminWrapper; - - public Object execute(ExecutionEvent event) throws ExecutionException { - String commandId = event.getParameter(PARAM_COMMAND_ID); - final UserTransaction userTransaction = userAdminWrapper - .getUserTransaction(); - try { - if (TRANSACTION_BEGIN.equals(commandId)) { - if (userTransaction.getStatus() != Status.STATUS_NO_TRANSACTION) - throw new CmsException("A transaction already exists"); - else - userTransaction.begin(); - } else if (TRANSACTION_COMMIT.equals(commandId)) { - if (userTransaction.getStatus() == Status.STATUS_NO_TRANSACTION) - throw new CmsException("No transaction."); - else - userTransaction.commit(); - } else if (TRANSACTION_ROLLBACK.equals(commandId)) { - if (userTransaction.getStatus() == Status.STATUS_NO_TRANSACTION) - throw new CmsException("No transaction to rollback."); - else { - userTransaction.rollback(); - userAdminWrapper.notifyListeners(new UserAdminEvent(null, - UserAdminEvent.ROLE_CHANGED, null)); - } - } - - UiAdminUtils.notifyTransactionStateChange(userTransaction); - // Try to remove invalid thread access errors when managing users. - // HandlerUtil.getActivePart(event).getSite().getShell().getDisplay() - // .asyncExec(new Runnable() { - // @Override - // public void run() { - // UiAdminUtils - // .notifyTransactionStateChange(userTransaction); - // } - // }); - - } catch (CmsException e) { - throw e; - } catch (Exception e) { - throw new CmsException("Unable to call " + commandId + " on " - + userTransaction, e); - } - return null; - } - - /* DEPENDENCY INJECTION */ - public void setUserAdminWrapper(UserAdminWrapper userAdminWrapper) { - this.userAdminWrapper = userAdminWrapper; - } -} \ No newline at end of file diff --git a/org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/internal/parts/ArgeoUserEditorInput.java b/org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/internal/parts/ArgeoUserEditorInput.java deleted file mode 100644 index cca2a1215..000000000 --- a/org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/internal/parts/ArgeoUserEditorInput.java +++ /dev/null @@ -1,65 +0,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. - */ -package org.argeo.security.ui.admin.internal.parts; - -import org.eclipse.jface.resource.ImageDescriptor; -import org.eclipse.ui.IEditorInput; -import org.eclipse.ui.IPersistableElement; - -/** Editor input for an Argeo user. */ -public class ArgeoUserEditorInput implements IEditorInput { - private final String username; - - public ArgeoUserEditorInput(String username) { - this.username = username; - } - - public Object getAdapter(@SuppressWarnings("rawtypes") Class adapter) { - return null; - } - - public boolean exists() { - return username != null; - } - - public ImageDescriptor getImageDescriptor() { - return null; - } - - public String getName() { - return username != null ? username : ""; - } - - public IPersistableElement getPersistable() { - return null; - } - - public String getToolTipText() { - return username != null ? username : ""; - } - - public boolean equals(Object obj) { - if (!(obj instanceof ArgeoUserEditorInput)) - return false; - if (((ArgeoUserEditorInput) obj).getUsername() == null) - return false; - return ((ArgeoUserEditorInput) obj).getUsername().equals(username); - } - - public String getUsername() { - return username; - } -} diff --git a/org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/internal/parts/DefaultUserMainPage.java b/org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/internal/parts/DefaultUserMainPage.java deleted file mode 100644 index 6529efe30..000000000 --- a/org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/internal/parts/DefaultUserMainPage.java +++ /dev/null @@ -1,243 +0,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. - */ -package org.argeo.security.ui.admin.internal.parts; - -import java.util.Arrays; - -import javax.jcr.Node; -import javax.jcr.Property; -import javax.jcr.RepositoryException; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.argeo.cms.CmsException; -import org.argeo.jcr.ArgeoNames; -import org.eclipse.swt.SWT; -import org.eclipse.swt.events.ModifyEvent; -import org.eclipse.swt.events.ModifyListener; -import org.eclipse.swt.layout.GridData; -import org.eclipse.swt.layout.GridLayout; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Label; -import org.eclipse.swt.widgets.Text; -import org.eclipse.ui.forms.AbstractFormPart; -import org.eclipse.ui.forms.IManagedForm; -import org.eclipse.ui.forms.SectionPart; -import org.eclipse.ui.forms.editor.FormEditor; -import org.eclipse.ui.forms.editor.FormPage; -import org.eclipse.ui.forms.widgets.FormToolkit; -import org.eclipse.ui.forms.widgets.ScrolledForm; -import org.eclipse.ui.forms.widgets.Section; - -/** Display/edit the properties common to all Argeo users */ -public class DefaultUserMainPage extends FormPage implements ArgeoNames { - final static String ID = "argeoUserEditor.mainPage"; - - private final static Log log = LogFactory.getLog(DefaultUserMainPage.class); - private Node userProfile; - - private char[] newPassword; - - public DefaultUserMainPage(FormEditor editor, Node userProfile) { - super(editor, ID, "Main"); - this.userProfile = userProfile; - } - - protected void createFormContent(final IManagedForm mf) { - try { - ScrolledForm form = mf.getForm(); - refreshFormTitle(form); - GridLayout mainLayout = new GridLayout(1, true); - form.getBody().setLayout(mainLayout); - - createGeneralPart(form.getBody()); - createPassworPart(form.getBody()); - } catch (RepositoryException e) { - throw new CmsException("Cannot create form content", e); - } - } - - /** Creates the general section */ - protected void createGeneralPart(Composite parent) - throws RepositoryException { - FormToolkit tk = getManagedForm().getToolkit(); - Section section = tk.createSection(parent, Section.TITLE_BAR); - section.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false)); - section.setText("General"); - Composite body = tk.createComposite(section, SWT.WRAP); - section.setClient(body); - GridLayout layout = new GridLayout(2, false); - body.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false)); - body.setLayout(layout); - - final Text commonName = createLT(body, "Displayed Name", - getProperty(Property.JCR_TITLE)); - final Text firstName = createLT(body, "First name", - getProperty(ARGEO_FIRST_NAME)); - final Text lastName = createLT(body, "Last name", - getProperty(ARGEO_LAST_NAME)); - final Text email = createLT(body, "Email", - getProperty(ARGEO_PRIMARY_EMAIL)); - final Text description = createLMT(body, "Description", - getProperty(Property.JCR_DESCRIPTION)); - - // create form part (controller) - AbstractFormPart part = new SectionPart(section) { - public void commit(boolean onSave) { - try { - userProfile.getSession().getWorkspace().getVersionManager() - .checkout(userProfile.getPath()); - userProfile.setProperty(Property.JCR_TITLE, - commonName.getText()); - userProfile.setProperty(ARGEO_FIRST_NAME, - firstName.getText()); - userProfile - .setProperty(ARGEO_LAST_NAME, lastName.getText()); - userProfile.setProperty(ARGEO_PRIMARY_EMAIL, - email.getText()); - userProfile.setProperty(Property.JCR_DESCRIPTION, - description.getText()); - userProfile.getSession().save(); - userProfile.getSession().getWorkspace().getVersionManager() - .checkin(userProfile.getPath()); - super.commit(onSave); - refreshFormTitle(getManagedForm().getForm()); - if (log.isTraceEnabled()) - log.trace("General part committed"); - } catch (RepositoryException e) { - throw new CmsException("Cannot commit", e); - } - } - }; - // if (username != null) - // username.addModifyListener(new FormPartML(part)); - commonName.addModifyListener(new FormPartML(part)); - firstName.addModifyListener(new FormPartML(part)); - lastName.addModifyListener(new FormPartML(part)); - email.addModifyListener(new FormPartML(part)); - description.addModifyListener(new FormPartML(part)); - getManagedForm().addPart(part); - } - - private void refreshFormTitle(ScrolledForm form) throws RepositoryException { - form.setText(getProperty(Property.JCR_TITLE) - + (userProfile.getProperty(ARGEO_ENABLED).getBoolean() ? "" - : " [DISABLED]")); - } - - /** @return the property, or the empty string if not set */ - protected String getProperty(String name) throws RepositoryException { - return userProfile.hasProperty(name) ? userProfile.getProperty(name) - .getString() : ""; - } - - /** Creates the password section */ - protected void createPassworPart(Composite parent) { - FormToolkit tk = getManagedForm().getToolkit(); - Section section = tk.createSection(parent, Section.TITLE_BAR); - section.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false)); - section.setText("Password"); - - Composite body = tk.createComposite(section, SWT.WRAP); - section.setClient(body); - GridLayout layout = new GridLayout(2, false); - body.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false)); - body.setLayout(layout); - - // add widgets (view) - final Text password1 = createLP(body, "New password", ""); - final Text password2 = createLP(body, "Repeat password", ""); - // create form part (controller) - AbstractFormPart part = new SectionPart(section) { - - public void commit(boolean onSave) { - if (!password1.getText().equals("") - || !password2.getText().equals("")) { - if (password1.getText().equals(password2.getText())) { - newPassword = password1.getText().toCharArray(); - password1.setText(""); - password2.setText(""); - super.commit(onSave); - } else { - password1.setText(""); - password2.setText(""); - throw new CmsException("Passwords are not equals"); - } - } - } - - }; - password1.addModifyListener(new FormPartML(part)); - password2.addModifyListener(new FormPartML(part)); - getManagedForm().addPart(part); - } - - /** Creates label and text. */ - protected Text createLT(Composite body, String label, String value) { - FormToolkit toolkit = getManagedForm().getToolkit(); - Label lbl = toolkit.createLabel(body, label); - lbl.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false)); - Text text = toolkit.createText(body, value, SWT.BORDER); - text.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false)); - return text; - } - - /** Creates label and multiline text. */ - protected Text createLMT(Composite body, String label, String value) { - FormToolkit toolkit = getManagedForm().getToolkit(); - Label lbl = toolkit.createLabel(body, label); - lbl.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false)); - Text text = toolkit.createText(body, value, SWT.BORDER | SWT.MULTI); - text.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, true)); - return text; - } - - /** Creates label and password. */ - protected Text createLP(Composite body, String label, String value) { - FormToolkit toolkit = getManagedForm().getToolkit(); - Label lbl = toolkit.createLabel(body, label); - lbl.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false)); - Text text = toolkit.createText(body, value, SWT.BORDER | SWT.PASSWORD); - text.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false)); - return text; - } - - private class FormPartML implements ModifyListener { - private static final long serialVersionUID = 6299808129505381333L; - private AbstractFormPart formPart; - - public FormPartML(AbstractFormPart generalPart) { - this.formPart = generalPart; - } - - public void modifyText(ModifyEvent e) { - formPart.markDirty(); - } - } - - public String getNewPassword() { - if (newPassword != null) - return new String(newPassword); - else - return null; - } - - public void resetNewPassword() { - if (newPassword != null) - Arrays.fill(newPassword, 'x'); - newPassword = null; - } -} diff --git a/org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/internal/parts/GroupMainPage.java b/org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/internal/parts/GroupMainPage.java deleted file mode 100644 index fe56679db..000000000 --- a/org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/internal/parts/GroupMainPage.java +++ /dev/null @@ -1,477 +0,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. - */ -package org.argeo.security.ui.admin.internal.parts; - -import java.util.ArrayList; -import java.util.Iterator; -import java.util.List; - -import javax.transaction.UserTransaction; - -import org.argeo.cms.CmsException; -import org.argeo.cms.util.useradmin.UserAdminUtils; -import org.argeo.eclipse.ui.ColumnDefinition; -import org.argeo.eclipse.ui.EclipseUiUtils; -import org.argeo.eclipse.ui.parts.LdifUsersTable; -import org.argeo.jcr.ArgeoNames; -import org.argeo.osgi.useradmin.LdifName; -import org.argeo.security.ui.admin.SecurityAdminImages; -import org.argeo.security.ui.admin.internal.UserAdminWrapper; -import org.argeo.security.ui.admin.internal.parts.UserEditor.GroupChangeListener; -import org.argeo.security.ui.admin.internal.parts.UserEditor.MainInfoListener; -import org.argeo.security.ui.admin.internal.providers.CommonNameLP; -import org.argeo.security.ui.admin.internal.providers.MailLP; -import org.argeo.security.ui.admin.internal.providers.RoleIconLP; -import org.argeo.security.ui.admin.internal.providers.UserFilter; -import org.argeo.security.ui.admin.internal.providers.UserNameLP; -import org.argeo.security.ui.admin.internal.providers.UserTableDefaultDClickListener; -import org.eclipse.jface.action.Action; -import org.eclipse.jface.action.ToolBarManager; -import org.eclipse.jface.dialogs.MessageDialog; -import org.eclipse.jface.resource.ImageDescriptor; -import org.eclipse.jface.viewers.ISelection; -import org.eclipse.jface.viewers.IStructuredSelection; -import org.eclipse.jface.viewers.TableViewer; -import org.eclipse.jface.viewers.ViewerDropAdapter; -import org.eclipse.swt.SWT; -import org.eclipse.swt.dnd.DND; -import org.eclipse.swt.dnd.DropTargetEvent; -import org.eclipse.swt.dnd.TextTransfer; -import org.eclipse.swt.dnd.Transfer; -import org.eclipse.swt.dnd.TransferData; -import org.eclipse.swt.events.DisposeEvent; -import org.eclipse.swt.events.DisposeListener; -import org.eclipse.swt.events.ModifyListener; -import org.eclipse.swt.graphics.Cursor; -import org.eclipse.swt.layout.GridData; -import org.eclipse.swt.layout.GridLayout; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Label; -import org.eclipse.swt.widgets.Shell; -import org.eclipse.swt.widgets.Text; -import org.eclipse.swt.widgets.ToolBar; -import org.eclipse.ui.forms.AbstractFormPart; -import org.eclipse.ui.forms.IManagedForm; -import org.eclipse.ui.forms.SectionPart; -import org.eclipse.ui.forms.editor.FormEditor; -import org.eclipse.ui.forms.editor.FormPage; -import org.eclipse.ui.forms.widgets.FormToolkit; -import org.eclipse.ui.forms.widgets.ScrolledForm; -import org.eclipse.ui.forms.widgets.Section; -import org.osgi.service.useradmin.Group; -import org.osgi.service.useradmin.Role; -import org.osgi.service.useradmin.User; -import org.osgi.service.useradmin.UserAdmin; -import org.osgi.service.useradmin.UserAdminEvent; - -/** Display/edit main properties of a given group */ -public class GroupMainPage extends FormPage implements ArgeoNames { - final static String ID = "GroupEditor.mainPage"; - - private final UserEditor editor; - private UserAdminWrapper userAdminWrapper; - - // Local configuration - private final int PRE_TITLE_INDENT = 10; - - public GroupMainPage(FormEditor editor, UserAdminWrapper userAdminWrapper) { - super(editor, ID, "Main"); - this.editor = (UserEditor) editor; - this.userAdminWrapper = userAdminWrapper; - } - - protected void createFormContent(final IManagedForm mf) { - ScrolledForm form = mf.getForm(); - Composite body = form.getBody(); - GridLayout mainLayout = new GridLayout(); - body.setLayout(mainLayout); - Group group = (Group) editor.getDisplayedUser(); - appendOverviewPart(body, group); - appendMembersPart(body, group); - } - - /** Creates the general section */ - protected void appendOverviewPart(final Composite parent, final Group group) { - FormToolkit tk = getManagedForm().getToolkit(); - Composite body = addSection(tk, parent, "Main information"); - GridLayout layout = new GridLayout(2, false); - body.setLayout(layout); - - final Text dnTxt = createLT(body, "DN", group.getName()); - dnTxt.setEnabled(false); - - final Text cnTxt = createLT(body, "Common Name", - UserAdminUtils.getProperty(group, LdifName.cn.name())); - cnTxt.setEnabled(false); - - Label descLbl = new Label(body, SWT.LEAD); - descLbl.setText("Description"); - descLbl.setLayoutData(new GridData(SWT.RIGHT, SWT.TOP, false, false)); - final Text descTxt = new Text(body, SWT.LEAD | SWT.MULTI | SWT.WRAP - | SWT.BORDER); - GridData gd = EclipseUiUtils.fillAll(); - gd.heightHint = 100; - descTxt.setLayoutData(gd); - - // create form part (controller) - AbstractFormPart part = new SectionPart((Section) body.getParent()) { - - private MainInfoListener listener; - - @Override - public void initialize(IManagedForm form) { - super.initialize(form); - listener = editor.new MainInfoListener(parent.getDisplay(), - this); - userAdminWrapper.addListener(listener); - } - - @Override - public void dispose() { - userAdminWrapper.removeListener(listener); - super.dispose(); - } - - @SuppressWarnings("unchecked") - public void commit(boolean onSave) { - group.getProperties().put(LdifName.description.name(), - descTxt.getText()); - // Enable common name ? - // editor.setProperty(UserAdminConstants.KEY_CN, - // email.getText()); - super.commit(onSave); - } - - @Override - public void refresh() { - refreshFormTitle(group); - dnTxt.setText(group.getName()); - cnTxt.setText(UserAdminUtils.getProperty(group, - LdifName.cn.name())); - descTxt.setText(UserAdminUtils.getProperty(group, - LdifName.description.name())); - super.refresh(); - } - }; - - ModifyListener defaultListener = editor.new FormPartML(part); - descTxt.addModifyListener(defaultListener); - getManagedForm().addPart(part); - } - - /** Filtered table with members. Has drag & drop ability */ - protected void appendMembersPart(Composite parent, Group group) { - - FormToolkit tk = getManagedForm().getToolkit(); - Section section = tk.createSection(parent, Section.TITLE_BAR); - section.setLayoutData(EclipseUiUtils.fillAll()); - - Composite body = new Composite(section, SWT.NO_FOCUS); - section.setClient(body); - body.setLayoutData(EclipseUiUtils.fillAll()); - - LdifUsersTable userTableViewerCmp = createMemberPart(body, group); - - SectionPart part = new GroupMembersPart(section, userTableViewerCmp, - group); - getManagedForm().addPart(part); - addRemoveAbitily(part, userTableViewerCmp.getTableViewer(), group); - } - - public LdifUsersTable createMemberPart(Composite parent, Group group) { - parent.setLayout(EclipseUiUtils.noSpaceGridLayout()); - - // Define the displayed columns - List columnDefs = new ArrayList(); - columnDefs.add(new ColumnDefinition(new RoleIconLP(), "", 0, 24)); - columnDefs.add(new ColumnDefinition(new CommonNameLP(), "Common Name", - 150)); - columnDefs.add(new ColumnDefinition(new MailLP(), "Primary Mail", 150)); - columnDefs.add(new ColumnDefinition(new UserNameLP(), - "Distinguished Name", 240)); - - // Create and configure the table - LdifUsersTable userViewerCmp = new MyUserTableViewer(parent, SWT.MULTI - | SWT.H_SCROLL | SWT.V_SCROLL, userAdminWrapper.getUserAdmin()); - - userViewerCmp.setColumnDefinitions(columnDefs); - userViewerCmp.populate(true, false); - userViewerCmp.setLayoutData(EclipseUiUtils.fillAll()); - - // Controllers - TableViewer userViewer = userViewerCmp.getTableViewer(); - userViewer.addDoubleClickListener(new UserTableDefaultDClickListener()); - int operations = DND.DROP_COPY | DND.DROP_MOVE; - Transfer[] tt = new Transfer[] { TextTransfer.getInstance() }; - userViewer.addDropSupport(operations, tt, - new GroupDropListener(userAdminWrapper, userViewerCmp, - (Group) editor.getDisplayedUser())); - - return userViewerCmp; - } - - // Local viewers - private class MyUserTableViewer extends LdifUsersTable { - private static final long serialVersionUID = 8467999509931900367L; - - private final UserFilter userFilter; - - public MyUserTableViewer(Composite parent, int style, - UserAdmin userAdmin) { - super(parent, style, true); - userFilter = new UserFilter(); - - } - - @Override - protected List listFilteredElements(String filter) { - Group group = (Group) editor.getDisplayedUser(); - Role[] roles = group.getMembers(); - List users = new ArrayList(); - userFilter.setSearchText(filter); - for (Role role : roles) - // if (role.getType() == Role.GROUP) - if (userFilter.select(null, null, role)) - users.add((User) role); - return users; - } - } - - private void addRemoveAbitily(SectionPart sectionPart, - TableViewer userViewer, Group group) { - Section section = sectionPart.getSection(); - ToolBarManager toolBarManager = new ToolBarManager(SWT.FLAT); - ToolBar toolbar = toolBarManager.createControl(section); - final Cursor handCursor = new Cursor(section.getDisplay(), - SWT.CURSOR_HAND); - toolbar.setCursor(handCursor); - toolbar.addDisposeListener(new DisposeListener() { - private static final long serialVersionUID = 3882131405820522925L; - - public void widgetDisposed(DisposeEvent e) { - if ((handCursor != null) && (handCursor.isDisposed() == false)) { - handCursor.dispose(); - } - } - }); - - Action action = new RemoveMembershipAction(userViewer, group, - "Remove selected items from this group", - SecurityAdminImages.ICON_REMOVE_DESC); - toolBarManager.add(action); - toolBarManager.update(true); - section.setTextClient(toolbar); - } - - private class RemoveMembershipAction extends Action { - private static final long serialVersionUID = -1337713097184522588L; - - private final TableViewer userViewer; - private final Group group; - - RemoveMembershipAction(TableViewer userViewer, Group group, - String name, ImageDescriptor img) { - super(name, img); - this.userViewer = userViewer; - this.group = group; - } - - @Override - public void run() { - ISelection selection = userViewer.getSelection(); - if (selection.isEmpty()) - return; - - @SuppressWarnings("unchecked") - Iterator it = ((IStructuredSelection) selection).iterator(); - List users = new ArrayList(); - while (it.hasNext()) { - User currUser = it.next(); - users.add(currUser); - } - - userAdminWrapper.beginTransactionIfNeeded(); - for (User user : users) { - group.removeMember(user); - } - userAdminWrapper.commitOrNotifyTransactionStateChange(); - userAdminWrapper.notifyListeners(new UserAdminEvent(null, - UserAdminEvent.ROLE_CHANGED, group)); - } - } - - // LOCAL CONTROLLERS - private class GroupMembersPart extends SectionPart { - private final LdifUsersTable userViewer; - private final Group group; - - private GroupChangeListener listener; - - public GroupMembersPart(Section section, LdifUsersTable userViewer, - Group group) { - super(section); - this.userViewer = userViewer; - this.group = group; - } - - @Override - public void initialize(IManagedForm form) { - super.initialize(form); - listener = editor.new GroupChangeListener(userViewer.getDisplay(), - GroupMembersPart.this); - userAdminWrapper.addListener(listener); - } - - @Override - public void dispose() { - userAdminWrapper.removeListener(listener); - super.dispose(); - } - - @Override - public void refresh() { - refreshFormTitle(group); - getSection().setText( - "Members of group " - + UserAdminUtils.getProperty(group, - LdifName.cn.name())); - userViewer.refresh(); - super.refresh(); - } - } - - /** - * Defines this table as being a potential target to add group membership - * (roles) to this group - */ - private class GroupDropListener extends ViewerDropAdapter { - private static final long serialVersionUID = 2893468717831451621L; - - private final UserAdminWrapper userAdminWrapper; - // private final LdifUsersTable myUserViewerCmp; - private final Group myGroup; - - public GroupDropListener(UserAdminWrapper userAdminWrapper, - LdifUsersTable userTableViewerCmp, Group group) { - super(userTableViewerCmp.getTableViewer()); - this.userAdminWrapper = userAdminWrapper; - this.myGroup = group; - // this.myUserViewerCmp = userTableViewerCmp; - } - - @Override - public boolean validateDrop(Object target, int operation, - TransferData transferType) { - // Target is always OK in a list only view - // TODO check if not a string - boolean validDrop = true; - return validDrop; - } - - @Override - public void drop(DropTargetEvent event) { - // TODO Is there an opportunity to perform the check before? - - String newUserName = (String) event.data; - UserAdmin myUserAdmin = userAdminWrapper.getUserAdmin(); - Role role = myUserAdmin.getRole(newUserName); - if (role.getType() == Role.GROUP) { - Group newGroup = (Group) role; - Shell shell = getViewer().getControl().getShell(); - // Sanity checks - if (myGroup == newGroup) { // Equality - MessageDialog.openError(shell, "Forbidden addition ", - "A group cannot be a member of itself."); - return; - } - - // Cycle - String myName = myGroup.getName(); - List myMemberships = editor.getFlatGroups(myGroup); - if (myMemberships.contains(newGroup)) { - MessageDialog.openError(shell, "Forbidden addition: cycle", - "Cannot add " + newUserName + " to group " + myName - + ". This would create a cycle"); - return; - } - - // Already member - List newGroupMemberships = editor.getFlatGroups(newGroup); - if (newGroupMemberships.contains(myGroup)) { - MessageDialog.openError(shell, "Forbidden addition", - "Cannot add " + newUserName + " to group " + myName - + ", this membership already exists"); - return; - } - userAdminWrapper.beginTransactionIfNeeded(); - myGroup.addMember(newGroup); - userAdminWrapper.commitOrNotifyTransactionStateChange(); - userAdminWrapper.notifyListeners(new UserAdminEvent(null, - UserAdminEvent.ROLE_CHANGED, myGroup)); - } else if (role.getType() == Role.USER) { - // TODO check if the group is already member of this group - UserTransaction transaction = userAdminWrapper - .beginTransactionIfNeeded(); - User user = (User) role; - myGroup.addMember(user); - if (UserAdminWrapper.COMMIT_ON_SAVE) - try { - transaction.commit(); - } catch (Exception e) { - throw new CmsException("Cannot commit transaction " - + "after user group membership update", e); - } - userAdminWrapper.notifyListeners(new UserAdminEvent(null, - UserAdminEvent.ROLE_CHANGED, myGroup)); - } - super.drop(event); - } - - @Override - public boolean performDrop(Object data) { - // myUserViewerCmp.refresh(); - return true; - } - } - - // LOCAL HELPERS - private void refreshFormTitle(Group group) { - getManagedForm().getForm().setText( - UserAdminUtils.getProperty(group, LdifName.cn.name())); - } - - private Composite addSection(FormToolkit tk, Composite parent, String title) { - Section section = tk.createSection(parent, Section.TITLE_BAR); - GridData gd = EclipseUiUtils.fillWidth(); - gd.verticalAlignment = PRE_TITLE_INDENT; - section.setLayoutData(gd); - section.setText(title); - Composite body = tk.createComposite(section, SWT.WRAP); - body.setLayoutData(EclipseUiUtils.fillAll()); - section.setClient(body); - return body; - } - - /** Creates label and text. */ - private Text createLT(Composite body, String label, String value) { - FormToolkit toolkit = getManagedForm().getToolkit(); - Label lbl = toolkit.createLabel(body, label); - lbl.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false)); - Text text = toolkit.createText(body, value, SWT.BORDER); - text.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false)); - return text; - } -} \ No newline at end of file diff --git a/org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/internal/parts/GroupsView.java b/org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/internal/parts/GroupsView.java deleted file mode 100644 index 82f4e1bdd..000000000 --- a/org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/internal/parts/GroupsView.java +++ /dev/null @@ -1,253 +0,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. - */ -package org.argeo.security.ui.admin.internal.parts; - -import java.util.ArrayList; -import java.util.List; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.argeo.cms.CmsException; -import org.argeo.cms.auth.AuthConstants; -import org.argeo.cms.util.useradmin.UserAdminUtils; -import org.argeo.eclipse.ui.ColumnDefinition; -import org.argeo.eclipse.ui.EclipseUiUtils; -import org.argeo.eclipse.ui.parts.LdifUsersTable; -import org.argeo.jcr.ArgeoNames; -import org.argeo.osgi.useradmin.LdifName; -import org.argeo.security.ui.admin.SecurityAdminPlugin; -import org.argeo.security.ui.admin.internal.UiUserAdminListener; -import org.argeo.security.ui.admin.internal.UserAdminWrapper; -import org.argeo.security.ui.admin.internal.providers.CommonNameLP; -import org.argeo.security.ui.admin.internal.providers.DomainNameLP; -import org.argeo.security.ui.admin.internal.providers.RoleIconLP; -import org.argeo.security.ui.admin.internal.providers.UserDragListener; -import org.argeo.security.ui.admin.internal.providers.UserNameLP; -import org.argeo.security.ui.admin.internal.providers.UserTableDefaultDClickListener; -import org.eclipse.jface.viewers.TableViewer; -import org.eclipse.swt.SWT; -import org.eclipse.swt.dnd.DND; -import org.eclipse.swt.dnd.TextTransfer; -import org.eclipse.swt.dnd.Transfer; -import org.eclipse.swt.events.SelectionAdapter; -import org.eclipse.swt.events.SelectionEvent; -import org.eclipse.swt.layout.GridLayout; -import org.eclipse.swt.widgets.Button; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Display; -import org.eclipse.ui.part.ViewPart; -import org.osgi.framework.InvalidSyntaxException; -import org.osgi.service.useradmin.Role; -import org.osgi.service.useradmin.User; -import org.osgi.service.useradmin.UserAdminEvent; -import org.osgi.service.useradmin.UserAdminListener; - -/** List all groups with filter */ -public class GroupsView extends ViewPart implements ArgeoNames { - private final static Log log = LogFactory.getLog(GroupsView.class); - public final static String ID = SecurityAdminPlugin.PLUGIN_ID - + ".groupsView"; - - /* DEPENDENCY INJECTION */ - private UserAdminWrapper userAdminWrapper; - - // UI Objects - private LdifUsersTable groupTableViewerCmp; - private TableViewer userViewer; - private List columnDefs = new ArrayList(); - - private UserAdminListener listener; - - @Override - public void createPartControl(Composite parent) { - parent.setLayout(EclipseUiUtils.noSpaceGridLayout()); - - boolean isAdmin = UserAdminUtils.isUserInRole(AuthConstants.ROLE_ADMIN); - - // Define the displayed columns - columnDefs.add(new ColumnDefinition(new RoleIconLP(), "", 26)); - columnDefs.add(new ColumnDefinition(new CommonNameLP(), "Common Name", - 150)); - columnDefs.add(new ColumnDefinition(new DomainNameLP(), "Domain", 200)); - // Only show technical DN to admin - if (isAdmin) - columnDefs.add(new ColumnDefinition(new UserNameLP(), - "Distinguished Name", 300)); - - // Create and configure the table - groupTableViewerCmp = new MyUserTableViewer(parent, SWT.MULTI - | SWT.H_SCROLL | SWT.V_SCROLL); - - groupTableViewerCmp.setColumnDefinitions(columnDefs); - if (isAdmin) - groupTableViewerCmp.populateWithStaticFilters(false, false); - else - groupTableViewerCmp.populate(true, false); - - groupTableViewerCmp.setLayoutData(EclipseUiUtils.fillAll()); - - // Links - userViewer = groupTableViewerCmp.getTableViewer(); - userViewer.addDoubleClickListener(new UserTableDefaultDClickListener()); - getViewSite().setSelectionProvider(userViewer); - - // Really? - groupTableViewerCmp.refresh(); - - // Drag and drop - int operations = DND.DROP_COPY | DND.DROP_MOVE; - Transfer[] tt = new Transfer[] { TextTransfer.getInstance() }; - userViewer.addDragSupport(operations, tt, new UserDragListener( - userViewer)); - - // // Register a useradmin listener - // listener = new UserAdminListener() { - // @Override - // public void roleChanged(UserAdminEvent event) { - // if (userViewer != null && !userViewer.getTable().isDisposed()) - // refresh(); - // } - // }; - // userAdminWrapper.addListener(listener); - // } - - // Register a useradmin listener - listener = new MyUiUAListener(parent.getDisplay()); - userAdminWrapper.addListener(listener); - } - - private class MyUiUAListener extends UiUserAdminListener { - public MyUiUAListener(Display display) { - super(display); - } - - @Override - public void roleChangedToUiThread(UserAdminEvent event) { - if (userViewer != null && !userViewer.getTable().isDisposed()) - refresh(); - } - } - - private class MyUserTableViewer extends LdifUsersTable { - private static final long serialVersionUID = 8467999509931900367L; - - private boolean showSystemRoles = false; - - private final String[] knownProps = { LdifName.uid.name(), - LdifName.cn.name(), LdifName.dn.name() }; - - public MyUserTableViewer(Composite parent, int style) { - super(parent, style); - } - - protected void populateStaticFilters(Composite staticFilterCmp) { - staticFilterCmp.setLayout(new GridLayout()); - final Button showSystemRoleBtn = new Button(staticFilterCmp, - SWT.CHECK); - showSystemRoleBtn.setText("Show system roles"); - showSystemRoleBtn.addSelectionListener(new SelectionAdapter() { - private static final long serialVersionUID = -7033424592697691676L; - - @Override - public void widgetSelected(SelectionEvent e) { - showSystemRoles = showSystemRoleBtn.getSelection(); - refresh(); - } - - }); - } - - @Override - protected List listFilteredElements(String filter) { - Role[] roles; - try { - StringBuilder builder = new StringBuilder(); - StringBuilder tmpBuilder = new StringBuilder(); - if (EclipseUiUtils.notEmpty(filter)) - for (String prop : knownProps) { - tmpBuilder.append("("); - tmpBuilder.append(prop); - tmpBuilder.append("=*"); - tmpBuilder.append(filter); - tmpBuilder.append("*)"); - } - if (tmpBuilder.length() > 1) { - builder.append("(&(").append(LdifName.objectClass.name()) - .append("=").append(LdifName.groupOfNames.name()) - .append(")"); - if (!showSystemRoles) - builder.append("(!(").append(LdifName.dn.name()) - .append("=*") - .append(AuthConstants.ROLES_BASEDN) - .append("))"); - builder.append("(|"); - builder.append(tmpBuilder.toString()); - builder.append("))"); - } else { - if (!showSystemRoles) - builder.append("(&(") - .append(LdifName.objectClass.name()) - .append("=") - .append(LdifName.groupOfNames.name()) - .append(")(!(").append(LdifName.dn.name()) - .append("=*") - .append(AuthConstants.ROLES_BASEDN) - .append(")))"); - else - builder.append("(").append(LdifName.objectClass.name()) - .append("=") - .append(LdifName.groupOfNames.name()) - .append(")"); - - } - roles = userAdminWrapper.getUserAdmin().getRoles( - builder.toString()); - } catch (InvalidSyntaxException e) { - throw new CmsException("Unable to get roles with filter: " - + filter, e); - } - List users = new ArrayList(); - for (Role role : roles) - if (!users.contains(role)) - users.add((User) role); - else - log.warn("Duplicated role: " + role); - - return users; - } - } - - public void refresh() { - groupTableViewerCmp.refresh(); - } - - // Override generic view methods - @Override - public void dispose() { - userAdminWrapper.removeListener(listener); - super.dispose(); - } - - @Override - public void setFocus() { - groupTableViewerCmp.setFocus(); - } - - /* DEPENDENCY INJECTION */ - public void setUserAdminWrapper(UserAdminWrapper userAdminWrapper) { - this.userAdminWrapper = userAdminWrapper; - } -} \ No newline at end of file diff --git a/org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/internal/parts/UserBatchUpdateWizard.java b/org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/internal/parts/UserBatchUpdateWizard.java deleted file mode 100644 index 31b2042b6..000000000 --- a/org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/internal/parts/UserBatchUpdateWizard.java +++ /dev/null @@ -1,578 +0,0 @@ -package org.argeo.security.ui.admin.internal.parts; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import javax.transaction.SystemException; -import javax.transaction.UserTransaction; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.argeo.cms.CmsException; -import org.argeo.cms.auth.AuthConstants; -import org.argeo.cms.util.useradmin.UserAdminUtils; -import org.argeo.eclipse.ui.ColumnDefinition; -import org.argeo.eclipse.ui.EclipseUiUtils; -import org.argeo.eclipse.ui.parts.LdifUsersTable; -import org.argeo.jcr.ArgeoNames; -import org.argeo.osgi.useradmin.LdifName; -import org.argeo.security.ui.admin.internal.UserAdminWrapper; -import org.argeo.security.ui.admin.internal.providers.CommonNameLP; -import org.argeo.security.ui.admin.internal.providers.DomainNameLP; -import org.argeo.security.ui.admin.internal.providers.MailLP; -import org.argeo.security.ui.admin.internal.providers.UserNameLP; -import org.eclipse.jface.dialogs.IPageChangeProvider; -import org.eclipse.jface.dialogs.IPageChangedListener; -import org.eclipse.jface.dialogs.MessageDialog; -import org.eclipse.jface.dialogs.PageChangedEvent; -import org.eclipse.jface.wizard.IWizardContainer; -import org.eclipse.jface.wizard.Wizard; -import org.eclipse.jface.wizard.WizardPage; -import org.eclipse.swt.SWT; -import org.eclipse.swt.events.ModifyEvent; -import org.eclipse.swt.events.ModifyListener; -import org.eclipse.swt.events.SelectionAdapter; -import org.eclipse.swt.events.SelectionEvent; -import org.eclipse.swt.layout.GridData; -import org.eclipse.swt.layout.GridLayout; -import org.eclipse.swt.widgets.Button; -import org.eclipse.swt.widgets.Combo; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Text; -import org.osgi.framework.InvalidSyntaxException; -import org.osgi.service.useradmin.Role; -import org.osgi.service.useradmin.User; - -/** Wizard to update users */ -public class UserBatchUpdateWizard extends Wizard { - - private final static Log log = LogFactory - .getLog(UserBatchUpdateWizard.class); - private UserAdminWrapper userAdminWrapper; - - // pages - private ChooseCommandWizardPage chooseCommandPage; - private ChooseUsersWizardPage userListPage; - private ValidateAndLaunchWizardPage validatePage; - - // Various implemented commands keys - private final static String CMD_UPDATE_PASSWORD = "resetPassword"; - private final static String CMD_GROUP_MEMBERSHIP = "groupMembership"; - - private final Map commands = new HashMap() { - private static final long serialVersionUID = 1L; - { - put("Reset password(s)", CMD_UPDATE_PASSWORD); - // TODO implement role / group management - // put("Add/Remove from group", CMD_GROUP_MEMBERSHIP); - } - }; - - public UserBatchUpdateWizard(UserAdminWrapper userAdminWrapper) { - this.userAdminWrapper = userAdminWrapper; - } - - @Override - public void addPages() { - chooseCommandPage = new ChooseCommandWizardPage(); - addPage(chooseCommandPage); - userListPage = new ChooseUsersWizardPage(); - addPage(userListPage); - validatePage = new ValidateAndLaunchWizardPage(); - addPage(validatePage); - } - - @Override - public boolean performFinish() { - if (!canFinish()) - return false; - UserTransaction ut = userAdminWrapper.getUserTransaction(); - try { - if (ut.getStatus() != javax.transaction.Status.STATUS_NO_TRANSACTION - && !MessageDialog.openConfirm(getShell(), - "Existing Transaction", - "A user transaction is already existing, " - + "are you sure you want to proceed ?")) - return false; - } catch (SystemException e) { - throw new CmsException("Cannot get user transaction state " - + "before user batch update", e); - } - - // We cannot use jobs, user modifications are still meant to be done in - // the UIThread - // UpdateJob job = null; - // if (job != null) - // job.schedule(); - - if (CMD_UPDATE_PASSWORD.equals(chooseCommandPage.getCommand())) { - char[] newValue = chooseCommandPage.getPwdValue(); - if (newValue == null) - throw new CmsException( - "Password cannot be null or an empty string"); - ResetPassword job = new ResetPassword(userAdminWrapper, - userListPage.getSelectedUsers(), newValue); - job.doUpdate(); - } - return true; - } - - public boolean canFinish() { - if (this.getContainer().getCurrentPage() == validatePage) - return true; - return false; - } - - private class ResetPassword { - private char[] newPwd; - private UserAdminWrapper userAdminWrapper; - private List usersToUpdate; - - public ResetPassword(UserAdminWrapper userAdminWrapper, - List usersToUpdate, char[] newPwd) { - this.newPwd = newPwd; - this.usersToUpdate = usersToUpdate; - this.userAdminWrapper = userAdminWrapper; - } - - @SuppressWarnings("unchecked") - protected void doUpdate() { - userAdminWrapper.beginTransactionIfNeeded(); - try { - for (User user : usersToUpdate) { - // the char array is emptied after being used. - user.getCredentials().put(null, newPwd.clone()); - } - userAdminWrapper.commitOrNotifyTransactionStateChange(); - } catch (Exception e) { - throw new CmsException("Cannot perform batch update on users", - e); - } finally { - UserTransaction ut = userAdminWrapper.getUserTransaction(); - try { - if (ut.getStatus() != javax.transaction.Status.STATUS_NO_TRANSACTION) - ut.rollback(); - } catch (IllegalStateException | SecurityException - | SystemException e) { - log.error("Unable to rollback session in 'finally', " - + "the system might be in a dirty state"); - e.printStackTrace(); - } - } - } - } - - // @SuppressWarnings("unused") - // private class AddToGroup extends UpdateJob { - // private String groupID; - // private Session session; - // - // public AddToGroup(Session session, List nodesToUpdate, - // String groupID) { - // super(session, nodesToUpdate); - // this.session = session; - // this.groupID = groupID; - // } - // - // protected void doUpdate(Node node) { - // log.info("Add/Remove to group actions are not yet implemented"); - // // TODO implement this - // // try { - // // throw new CmsException("Not yet implemented"); - // // } catch (RepositoryException re) { - // // throw new CmsException( - // // "Unable to update boolean value for node " + node, re); - // // } - // } - // } - - // /** - // * Base privileged job that will be run asynchronously to perform the - // batch - // * update - // */ - // private abstract class UpdateJob extends PrivilegedJob { - // - // private final UserAdminWrapper userAdminWrapper; - // private final List usersToUpdate; - // - // protected abstract void doUpdate(User user); - // - // public UpdateJob(UserAdminWrapper userAdminWrapper, - // List usersToUpdate) { - // super("Perform update"); - // this.usersToUpdate = usersToUpdate; - // this.userAdminWrapper = userAdminWrapper; - // } - // - // @Override - // protected IStatus doRun(IProgressMonitor progressMonitor) { - // try { - // ArgeoMonitor monitor = new EclipseArgeoMonitor(progressMonitor); - // int total = usersToUpdate.size(); - // monitor.beginTask("Performing change", total); - // userAdminWrapper.beginTransactionIfNeeded(); - // for (User user : usersToUpdate) { - // doUpdate(user); - // monitor.worked(1); - // } - // userAdminWrapper.getUserTransaction().commit(); - // } catch (Exception e) { - // throw new CmsException( - // "Cannot perform batch update on users", e); - // } finally { - // UserTransaction ut = userAdminWrapper.getUserTransaction(); - // try { - // if (ut.getStatus() != javax.transaction.Status.STATUS_NO_TRANSACTION) - // ut.rollback(); - // } catch (IllegalStateException | SecurityException - // | SystemException e) { - // log.error("Unable to rollback session in 'finally', " - // + "the system might be in a dirty state"); - // e.printStackTrace(); - // } - // } - // return Status.OK_STATUS; - // } - // } - - // PAGES - /** Displays a combo box that enables user to choose which action to perform */ - private class ChooseCommandWizardPage extends WizardPage { - private static final long serialVersionUID = -8069434295293996633L; - private Combo chooseCommandCmb; - private Button trueChk; - private Text valueTxt; - private Text pwdTxt; - private Text pwd2Txt; - - public ChooseCommandWizardPage() { - super("Choose a command to run."); - setTitle("Choose a command to run."); - } - - @Override - public void createControl(Composite parent) { - GridLayout gl = new GridLayout(); - Composite container = new Composite(parent, SWT.NO_FOCUS); - container.setLayout(gl); - - chooseCommandCmb = new Combo(container, SWT.READ_ONLY); - chooseCommandCmb.setLayoutData(EclipseUiUtils.fillWidth()); - String[] values = commands.keySet().toArray(new String[0]); - chooseCommandCmb.setItems(values); - - final Composite bottomPart = new Composite(container, SWT.NO_FOCUS); - bottomPart.setLayoutData(EclipseUiUtils.fillAll()); - bottomPart.setLayout(EclipseUiUtils.noSpaceGridLayout()); - - chooseCommandCmb.addSelectionListener(new SelectionAdapter() { - private static final long serialVersionUID = 1L; - - @Override - public void widgetSelected(SelectionEvent e) { - if (getCommand().equals(CMD_UPDATE_PASSWORD)) - populatePasswordCmp(bottomPart); - else if (getCommand().equals(CMD_GROUP_MEMBERSHIP)) - populateGroupCmp(bottomPart); - else - populateBooleanFlagCmp(bottomPart); - checkPageComplete(); - bottomPart.layout(true, true); - } - }); - setControl(container); - } - - private void populateBooleanFlagCmp(Composite parent) { - EclipseUiUtils.clear(parent); - trueChk = new Button(parent, SWT.CHECK); - trueChk.setText("Do it. (It will to the contrary if unchecked)"); - trueChk.setSelection(true); - trueChk.setLayoutData(new GridData(SWT.LEFT, SWT.TOP, false, false)); - } - - private void populatePasswordCmp(Composite parent) { - EclipseUiUtils.clear(parent); - Composite body = new Composite(parent, SWT.NO_FOCUS); - - ModifyListener ml = new ModifyListener() { - private static final long serialVersionUID = -1558726363536729634L; - - @Override - public void modifyText(ModifyEvent event) { - checkPageComplete(); - } - }; - - body.setLayout(new GridLayout(2, false)); - body.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); - pwdTxt = EclipseUiUtils.createGridLP(body, "New password", ml); - pwd2Txt = EclipseUiUtils.createGridLP(body, "Repeat password", ml); - } - - private void checkPageComplete() { - String errorMsg = null; - if (chooseCommandCmb.getSelectionIndex() < 0) - errorMsg = "Please select an action"; - else if (CMD_UPDATE_PASSWORD.equals(getCommand())) { - if (EclipseUiUtils.isEmpty(pwdTxt.getText()) - || pwdTxt.getText().length() < 4) - errorMsg = "Please enter a password that is at least 4 character long"; - else if (!pwdTxt.getText().equals(pwd2Txt.getText())) - errorMsg = "Passwords are different"; - } - if (EclipseUiUtils.notEmpty(errorMsg)) { - setMessage(errorMsg, WizardPage.ERROR); - setPageComplete(false); - } else { - setMessage("Page complete, you can proceed to user choice", - WizardPage.INFORMATION); - setPageComplete(true); - } - - getContainer().updateButtons(); - } - - private void populateGroupCmp(Composite parent) { - EclipseUiUtils.clear(parent); - trueChk = new Button(parent, SWT.CHECK); - trueChk.setText("Add to group. (It will remove user(s) from the " - + "corresponding group if unchecked)"); - trueChk.setSelection(true); - trueChk.setLayoutData(new GridData(SWT.LEFT, SWT.TOP, false, false)); - } - - protected String getCommand() { - return commands.get(chooseCommandCmb.getItem(chooseCommandCmb - .getSelectionIndex())); - } - - protected String getCommandLbl() { - return chooseCommandCmb.getItem(chooseCommandCmb - .getSelectionIndex()); - } - - @SuppressWarnings("unused") - protected boolean getBoleanValue() { - // FIXME this is not consistent and will lead to errors. - if (ArgeoNames.ARGEO_ENABLED.equals(getCommand())) - return trueChk.getSelection(); - else - return !trueChk.getSelection(); - } - - @SuppressWarnings("unused") - protected String getStringValue() { - String value = null; - if (valueTxt != null) { - value = valueTxt.getText(); - if ("".equals(value.trim())) - value = null; - } - return value; - } - - protected char[] getPwdValue() { - // We do not directly reset the password text fields: There is no - // need to over secure this process: setting a pwd to multi users - // at the same time is anyhow a bad practice and should be used only - // in test environment or for temporary access - if (pwdTxt == null || pwdTxt.isDisposed()) - return null; - else - return pwdTxt.getText().toCharArray(); - } - } - - /** - * Displays a list of users with a check box to be able to choose some of - * them - */ - private class ChooseUsersWizardPage extends WizardPage implements - IPageChangedListener { - private static final long serialVersionUID = 7651807402211214274L; - private ChooseUserTableViewer userTableCmp; - - public ChooseUsersWizardPage() { - super("Choose Users"); - setTitle("Select users who will be impacted"); - } - - @Override - public void createControl(Composite parent) { - Composite pageCmp = new Composite(parent, SWT.NONE); - pageCmp.setLayout(EclipseUiUtils.noSpaceGridLayout()); - - // Define the displayed columns - List columnDefs = new ArrayList(); - columnDefs.add(new ColumnDefinition(new CommonNameLP(), - "Common Name", 150)); - columnDefs.add(new ColumnDefinition(new MailLP(), "E-mail", 150)); - columnDefs.add(new ColumnDefinition(new DomainNameLP(), "Domain", - 200)); - - // Only show technical DN to admin - if (UserAdminUtils.isUserInRole(AuthConstants.ROLE_ADMIN)) - columnDefs.add(new ColumnDefinition(new UserNameLP(), - "Distinguished Name", 300)); - - userTableCmp = new ChooseUserTableViewer(pageCmp, SWT.MULTI - | SWT.H_SCROLL | SWT.V_SCROLL); - userTableCmp.setLayoutData(EclipseUiUtils.fillAll()); - userTableCmp.setColumnDefinitions(columnDefs); - userTableCmp.populate(true, true); - userTableCmp.refresh(); - - setControl(pageCmp); - - // Add listener to update message when shown - final IWizardContainer wContainer = this.getContainer(); - if (wContainer instanceof IPageChangeProvider) { - ((IPageChangeProvider) wContainer).addPageChangedListener(this); - } - - } - - @Override - public void pageChanged(PageChangedEvent event) { - if (event.getSelectedPage() == this) { - String msg = "Chosen batch action: " - + chooseCommandPage.getCommandLbl(); - ((WizardPage) event.getSelectedPage()).setMessage(msg); - } - } - - protected List getSelectedUsers() { - return userTableCmp.getSelectedUsers(); - } - - private class ChooseUserTableViewer extends LdifUsersTable { - private static final long serialVersionUID = 5080437561015853124L; - private final String[] knownProps = { LdifName.uid.name(), - LdifName.dn.name(), LdifName.cn.name(), - LdifName.givenName.name(), LdifName.sn.name(), - LdifName.mail.name() }; - - public ChooseUserTableViewer(Composite parent, int style) { - super(parent, style); - } - - @Override - protected List listFilteredElements(String filter) { - Role[] roles; - - try { - StringBuilder builder = new StringBuilder(); - - StringBuilder tmpBuilder = new StringBuilder(); - if (EclipseUiUtils.notEmpty(filter)) - for (String prop : knownProps) { - tmpBuilder.append("("); - tmpBuilder.append(prop); - tmpBuilder.append("=*"); - tmpBuilder.append(filter); - tmpBuilder.append("*)"); - } - if (tmpBuilder.length() > 1) { - builder.append("(&(") - .append(LdifName.objectClass.name()) - .append("=") - .append(LdifName.inetOrgPerson.name()) - .append(")(|"); - builder.append(tmpBuilder.toString()); - builder.append("))"); - } else - builder.append("(").append(LdifName.objectClass.name()) - .append("=") - .append(LdifName.inetOrgPerson.name()) - .append(")"); - roles = userAdminWrapper.getUserAdmin().getRoles( - builder.toString()); - } catch (InvalidSyntaxException e) { - throw new CmsException("Unable to get roles with filter: " - + filter, e); - } - List users = new ArrayList(); - for (Role role : roles) - // Prevent current logged in user to perform batch on - // himself - if (!UserAdminUtils.isCurrentUser((User) role)) - users.add((User) role); - return users; - } - } - } - - /** Summary of input data before launching the process */ - private class ValidateAndLaunchWizardPage extends WizardPage implements - IPageChangedListener { - private static final long serialVersionUID = 7098918351451743853L; - private ChosenUsersTableViewer userTableCmp; - - public ValidateAndLaunchWizardPage() { - super("Validate and launch"); - setTitle("Validate and launch"); - } - - @Override - public void createControl(Composite parent) { - Composite pageCmp = new Composite(parent, SWT.NO_FOCUS); - pageCmp.setLayout(EclipseUiUtils.noSpaceGridLayout()); - - List columnDefs = new ArrayList(); - columnDefs.add(new ColumnDefinition(new CommonNameLP(), - "Common Name", 150)); - columnDefs.add(new ColumnDefinition(new MailLP(), "E-mail", 150)); - columnDefs.add(new ColumnDefinition(new DomainNameLP(), "Domain", - 200)); - // Only show technical DN to admin - if (UserAdminUtils.isUserInRole(AuthConstants.ROLE_ADMIN)) - columnDefs.add(new ColumnDefinition(new UserNameLP(), - "Distinguished Name", 300)); - userTableCmp = new ChosenUsersTableViewer(pageCmp, SWT.MULTI - | SWT.H_SCROLL | SWT.V_SCROLL); - userTableCmp.setLayoutData(EclipseUiUtils.fillAll()); - userTableCmp.setColumnDefinitions(columnDefs); - userTableCmp.populate(false, false); - userTableCmp.refresh(); - setControl(pageCmp); - // Add listener to update message when shown - final IWizardContainer wContainer = this.getContainer(); - if (wContainer instanceof IPageChangeProvider) { - ((IPageChangeProvider) wContainer).addPageChangedListener(this); - } - } - - @Override - public void pageChanged(PageChangedEvent event) { - if (event.getSelectedPage() == this) { - @SuppressWarnings({ "unchecked", "rawtypes" }) - Object[] values = ((ArrayList) userListPage.getSelectedUsers()) - .toArray(new Object[userListPage.getSelectedUsers() - .size()]); - userTableCmp.getTableViewer().setInput(values); - String msg = "Following batch action: [" - + chooseCommandPage.getCommandLbl() - + "] will be perfomed on the users listed below.\n"; - // + "Are you sure you want to proceed?"; - setMessage(msg); - } - } - - private class ChosenUsersTableViewer extends LdifUsersTable { - private static final long serialVersionUID = 7814764735794270541L; - - public ChosenUsersTableViewer(Composite parent, int style) { - super(parent, style); - } - - @Override - protected List listFilteredElements(String filter) { - return userListPage.getSelectedUsers(); - } - } - } -} \ No newline at end of file diff --git a/org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/internal/parts/UserEditor.java b/org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/internal/parts/UserEditor.java deleted file mode 100644 index 6c0731d01..000000000 --- a/org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/internal/parts/UserEditor.java +++ /dev/null @@ -1,217 +0,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. - */ -package org.argeo.security.ui.admin.internal.parts; - -import java.util.ArrayList; -import java.util.List; - -import org.argeo.cms.CmsException; -import org.argeo.cms.util.useradmin.UserAdminUtils; -import org.argeo.osgi.useradmin.LdifName; -import org.argeo.security.ui.admin.SecurityAdminPlugin; -import org.argeo.security.ui.admin.internal.UiUserAdminListener; -import org.argeo.security.ui.admin.internal.UserAdminWrapper; -import org.eclipse.core.runtime.IProgressMonitor; -import org.eclipse.swt.events.ModifyEvent; -import org.eclipse.swt.events.ModifyListener; -import org.eclipse.swt.widgets.Control; -import org.eclipse.swt.widgets.Display; -import org.eclipse.ui.IEditorInput; -import org.eclipse.ui.IEditorSite; -import org.eclipse.ui.PartInitException; -import org.eclipse.ui.forms.AbstractFormPart; -import org.eclipse.ui.forms.editor.FormEditor; -import org.osgi.service.useradmin.Authorization; -import org.osgi.service.useradmin.Role; -import org.osgi.service.useradmin.User; -import org.osgi.service.useradmin.UserAdmin; -import org.osgi.service.useradmin.UserAdminEvent; - -/** Editor for a user, might be a user or a group. */ -public class UserEditor extends FormEditor { - private static final long serialVersionUID = 8357851520380820241L; - - public final static String USER_EDITOR_ID = SecurityAdminPlugin.PLUGIN_ID - + ".userEditor"; - public final static String GROUP_EDITOR_ID = SecurityAdminPlugin.PLUGIN_ID - + ".groupEditor"; - - /* DEPENDENCY INJECTION */ - private UserAdminWrapper userAdminWrapper; - private UserAdmin userAdmin; - - // Context - private User user; - private String username; - - private NameChangeListener listener; - - public void init(IEditorSite site, IEditorInput input) - throws PartInitException { - super.init(site, input); - username = ((UserEditorInput) getEditorInput()).getUsername(); - user = (User) userAdmin.getRole(username); - - listener = new NameChangeListener(site.getShell().getDisplay(), user); - userAdminWrapper.addListener(listener); - updateEditorTitle(null); - } - - /** - * returns the list of all authorization for the given user or of the - * current displayed user if parameter is null - */ - protected List getFlatGroups(User aUser) { - Authorization currAuth; - if (aUser == null) - currAuth = userAdmin.getAuthorization(this.user); - else - currAuth = userAdmin.getAuthorization(aUser); - - String[] roles = currAuth.getRoles(); - - List groups = new ArrayList(); - for (String roleStr : roles) { - User currRole = (User) userAdmin.getRole(roleStr); - if (!groups.contains(currRole)) - groups.add(currRole); - } - return groups; - } - - /** Exposes the user (or group) that is displayed by the current editor */ - protected User getDisplayedUser() { - return user; - } - - void updateEditorTitle(String title) { - if (title == null) { - String commonName = UserAdminUtils.getProperty(user, - LdifName.cn.name()); - title = "".equals(commonName) ? user.getName() : commonName; - } - setPartName(title); - } - - protected void addPages() { - try { - if (user.getType() == Role.GROUP) - addPage(new GroupMainPage(this, userAdminWrapper)); - else - addPage(new UserMainPage(this, userAdminWrapper)); - } catch (Exception e) { - throw new CmsException("Cannot add pages", e); - } - } - - @Override - public void doSave(IProgressMonitor monitor) { - userAdminWrapper.beginTransactionIfNeeded(); - commitPages(true); - userAdminWrapper.commitOrNotifyTransactionStateChange(); - firePropertyChange(PROP_DIRTY); - userAdminWrapper.notifyListeners(new UserAdminEvent(null, - UserAdminEvent.ROLE_REMOVED, user)); - } - - @Override - public void doSaveAs() { - } - - @Override - public boolean isSaveAsAllowed() { - return false; - } - - @Override - public void dispose() { - userAdminWrapper.removeListener(listener); - super.dispose(); - } - - // CONTROLERS FOR THIS EDITOR AND ITS PAGES - - private class NameChangeListener extends UiUserAdminListener { - - private final User user; - - public NameChangeListener(Display display, User user) { - super(display); - this.user = user; - } - - @Override - public void roleChangedToUiThread(UserAdminEvent event) { - Role changedRole = event.getRole(); - if (changedRole == null || changedRole.equals(user)) - updateEditorTitle(null); - } - } - - class MainInfoListener extends UiUserAdminListener { - private final AbstractFormPart part; - - public MainInfoListener(Display display, AbstractFormPart part) { - super(display); - this.part = part; - } - - @Override - public void roleChangedToUiThread(UserAdminEvent event) { - // Rollback - if (event.getRole() == null) - part.markStale(); - } - } - - class GroupChangeListener extends UiUserAdminListener { - private final AbstractFormPart part; - - public GroupChangeListener(Display display, AbstractFormPart part) { - super(display); - this.part = part; - } - - @Override - public void roleChangedToUiThread(UserAdminEvent event) { - // always mark as stale - part.markStale(); - } - } - - /** Registers a listener that will notify this part */ - class FormPartML implements ModifyListener { - private static final long serialVersionUID = 6299808129505381333L; - private AbstractFormPart formPart; - - public FormPartML(AbstractFormPart generalPart) { - this.formPart = generalPart; - } - - public void modifyText(ModifyEvent e) { - // Discard event when the control does not have the focus, typically - // to avoid all editors being marked as dirty during a Rollback - if (((Control) e.widget).isFocusControl()) - formPart.markDirty(); - } - } - - /* DEPENDENCY INJECTION */ - public void setUserAdminWrapper(UserAdminWrapper userAdminWrapper) { - this.userAdminWrapper = userAdminWrapper; - this.userAdmin = userAdminWrapper.getUserAdmin(); - } -} \ No newline at end of file diff --git a/org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/internal/parts/UserEditorInput.java b/org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/internal/parts/UserEditorInput.java deleted file mode 100644 index fe129f815..000000000 --- a/org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/internal/parts/UserEditorInput.java +++ /dev/null @@ -1,68 +0,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. - */ -package org.argeo.security.ui.admin.internal.parts; - -import org.eclipse.jface.resource.ImageDescriptor; -import org.eclipse.ui.IEditorInput; -import org.eclipse.ui.IPersistableElement; - -/** - * Editor input for an user defined by unique name (usually a distinguished - * name). - */ -public class UserEditorInput implements IEditorInput { - private final String username; - - public UserEditorInput(String username) { - this.username = username; - } - - public Object getAdapter(@SuppressWarnings("rawtypes") Class adapter) { - return null; - } - - public boolean exists() { - return username != null; - } - - public ImageDescriptor getImageDescriptor() { - return null; - } - - public String getName() { - return username != null ? username : ""; - } - - public IPersistableElement getPersistable() { - return null; - } - - public String getToolTipText() { - return username != null ? username : ""; - } - - public boolean equals(Object obj) { - if (!(obj instanceof UserEditorInput)) - return false; - if (((UserEditorInput) obj).getUsername() == null) - return false; - return ((UserEditorInput) obj).getUsername().equals(username); - } - - public String getUsername() { - return username; - } -} \ No newline at end of file diff --git a/org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/internal/parts/UserMainPage.java b/org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/internal/parts/UserMainPage.java deleted file mode 100644 index db6eb538f..000000000 --- a/org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/internal/parts/UserMainPage.java +++ /dev/null @@ -1,539 +0,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. - */ -package org.argeo.security.ui.admin.internal.parts; - -import java.util.ArrayList; -import java.util.Iterator; -import java.util.List; - -import org.argeo.cms.CmsException; -import org.argeo.cms.auth.AuthConstants; -import org.argeo.cms.util.useradmin.UserAdminUtils; -import org.argeo.eclipse.ui.ColumnDefinition; -import org.argeo.eclipse.ui.EclipseUiUtils; -import org.argeo.eclipse.ui.parts.LdifUsersTable; -import org.argeo.jcr.ArgeoNames; -import org.argeo.osgi.useradmin.LdifName; -import org.argeo.security.ui.admin.SecurityAdminImages; -import org.argeo.security.ui.admin.internal.UserAdminWrapper; -import org.argeo.security.ui.admin.internal.parts.UserEditor.GroupChangeListener; -import org.argeo.security.ui.admin.internal.parts.UserEditor.MainInfoListener; -import org.argeo.security.ui.admin.internal.providers.CommonNameLP; -import org.argeo.security.ui.admin.internal.providers.DomainNameLP; -import org.argeo.security.ui.admin.internal.providers.RoleIconLP; -import org.argeo.security.ui.admin.internal.providers.UserFilter; -import org.argeo.security.ui.admin.internal.providers.UserNameLP; -import org.argeo.security.ui.admin.internal.providers.UserTableDefaultDClickListener; -import org.eclipse.jface.action.Action; -import org.eclipse.jface.action.ToolBarManager; -import org.eclipse.jface.resource.ImageDescriptor; -import org.eclipse.jface.viewers.ISelection; -import org.eclipse.jface.viewers.IStructuredSelection; -import org.eclipse.jface.viewers.TableViewer; -import org.eclipse.jface.viewers.Viewer; -import org.eclipse.jface.viewers.ViewerDropAdapter; -import org.eclipse.swt.SWT; -import org.eclipse.swt.dnd.DND; -import org.eclipse.swt.dnd.DropTargetEvent; -import org.eclipse.swt.dnd.TextTransfer; -import org.eclipse.swt.dnd.Transfer; -import org.eclipse.swt.dnd.TransferData; -import org.eclipse.swt.events.DisposeEvent; -import org.eclipse.swt.events.DisposeListener; -import org.eclipse.swt.events.ModifyEvent; -import org.eclipse.swt.events.ModifyListener; -import org.eclipse.swt.events.SelectionAdapter; -import org.eclipse.swt.events.SelectionEvent; -import org.eclipse.swt.graphics.Cursor; -import org.eclipse.swt.layout.GridData; -import org.eclipse.swt.layout.GridLayout; -import org.eclipse.swt.widgets.Button; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Label; -import org.eclipse.swt.widgets.Text; -import org.eclipse.swt.widgets.ToolBar; -import org.eclipse.ui.forms.AbstractFormPart; -import org.eclipse.ui.forms.IManagedForm; -import org.eclipse.ui.forms.SectionPart; -import org.eclipse.ui.forms.editor.FormEditor; -import org.eclipse.ui.forms.editor.FormPage; -import org.eclipse.ui.forms.widgets.FormToolkit; -import org.eclipse.ui.forms.widgets.ScrolledForm; -import org.eclipse.ui.forms.widgets.Section; -import org.osgi.service.useradmin.Group; -import org.osgi.service.useradmin.Role; -import org.osgi.service.useradmin.User; -import org.osgi.service.useradmin.UserAdmin; -import org.osgi.service.useradmin.UserAdminEvent; - -/** Display/edit the properties of a given user */ -public class UserMainPage extends FormPage implements ArgeoNames { - final static String ID = "UserEditor.mainPage"; - - private final UserEditor editor; - private UserAdminWrapper userAdminWrapper; - - // Local configuration - private final int PRE_TITLE_INDENT = 10; - - public UserMainPage(FormEditor editor, UserAdminWrapper userAdminWrapper) { - super(editor, ID, "Main"); - this.editor = (UserEditor) editor; - this.userAdminWrapper = userAdminWrapper; - } - - protected void createFormContent(final IManagedForm mf) { - ScrolledForm form = mf.getForm(); - Composite body = form.getBody(); - GridLayout mainLayout = new GridLayout(); - // mainLayout.marginRight = 10; - body.setLayout(mainLayout); - User user = editor.getDisplayedUser(); - appendOverviewPart(body, user); - // Remove to ability to force the password for his own user. The user - // must then use the change pwd feature - if (!UserAdminUtils.isCurrentUser(user)) - appendPasswordPart(body, user); - appendMemberOfPart(body, user); - } - - /** Creates the general section */ - private void appendOverviewPart(final Composite parent, final User user) { - FormToolkit tk = getManagedForm().getToolkit(); - - Section section = addSection(tk, parent, "Main information"); - Composite body = (Composite) section.getClient(); - body.setLayout(new GridLayout(2, false)); - - final Text distinguishedName = createLT(tk, body, "User Name", - UserAdminUtils.getProperty(user, LdifName.uid.name())); - distinguishedName.setEnabled(false); - - final Text commonName = createLT(tk, body, "Common Name", - UserAdminUtils.getProperty(user, LdifName.cn.name())); - commonName.setEnabled(false); - - final Text firstName = createLT(tk, body, "First name", - UserAdminUtils.getProperty(user, LdifName.givenName.name())); - - final Text lastName = createLT(tk, body, "Last name", - UserAdminUtils.getProperty(user, LdifName.sn.name())); - - final Text email = createLT(tk, body, "Email", - UserAdminUtils.getProperty(user, LdifName.mail.name())); - - // create form part (controller) - AbstractFormPart part = new SectionPart((Section) body.getParent()) { - private MainInfoListener listener; - - @Override - public void initialize(IManagedForm form) { - super.initialize(form); - listener = editor.new MainInfoListener(parent.getDisplay(), - this); - userAdminWrapper.addListener(listener); - } - - @Override - public void dispose() { - userAdminWrapper.removeListener(listener); - super.dispose(); - } - - @SuppressWarnings("unchecked") - public void commit(boolean onSave) { - // TODO Sanity checks (mail validity...) - user.getProperties().put(LdifName.givenName.name(), - firstName.getText()); - user.getProperties() - .put(LdifName.sn.name(), lastName.getText()); - user.getProperties().put(LdifName.cn.name(), - commonName.getText()); - user.getProperties().put(LdifName.mail.name(), email.getText()); - super.commit(onSave); - } - - @Override - public void refresh() { - distinguishedName.setText(UserAdminUtils.getProperty(user, - LdifName.uid.name())); - commonName.setText(UserAdminUtils.getProperty(user, - LdifName.cn.name())); - firstName.setText(UserAdminUtils.getProperty(user, - LdifName.givenName.name())); - lastName.setText(UserAdminUtils.getProperty(user, - LdifName.sn.name())); - email.setText(UserAdminUtils.getProperty(user, - LdifName.mail.name())); - refreshFormTitle(user); - super.refresh(); - } - }; - - // Improve this: automatically generate CN when first or last name - // changes - ModifyListener cnML = new ModifyListener() { - private static final long serialVersionUID = 4298649222869835486L; - - @Override - public void modifyText(ModifyEvent event) { - String first = firstName.getText(); - String last = lastName.getText(); - String cn = first.trim() + " " + last.trim() + " "; - cn = cn.trim(); - commonName.setText(cn); - getManagedForm().getForm().setText(cn); - editor.updateEditorTitle(cn); - } - }; - firstName.addModifyListener(cnML); - lastName.addModifyListener(cnML); - - ModifyListener defaultListener = editor.new FormPartML(part); - firstName.addModifyListener(defaultListener); - lastName.addModifyListener(defaultListener); - email.addModifyListener(defaultListener); - getManagedForm().addPart(part); - } - - /** Creates the password section */ - private void appendPasswordPart(Composite parent, final User user) { - FormToolkit tk = getManagedForm().getToolkit(); - Section section = addSection(tk, parent, "Password"); - Composite body = (Composite) section.getClient(); - body.setLayout(new GridLayout(2, false)); - - // add widgets (view) - final Text password1 = createLP(tk, body, "New password", ""); - final Text password2 = createLP(tk, body, "Repeat password", ""); - - // create form part (controller) - AbstractFormPart part = new SectionPart((Section) body.getParent()) { - @SuppressWarnings("unchecked") - public void commit(boolean onSave) { - if (!password1.getText().equals("") - || !password2.getText().equals("")) { - if (password1.getText().equals(password2.getText())) { - char[] newPassword = password1.getText().toCharArray(); - // userAdminWrapper.beginTransactionIfNeeded(); - user.getCredentials().put(null, newPassword); - password1.setText(""); - password2.setText(""); - super.commit(onSave); - } else { - password1.setText(""); - password2.setText(""); - throw new CmsException("Passwords are not equals"); - } - } - } - }; - ModifyListener defaultListener = editor.new FormPartML(part); - password1.addModifyListener(defaultListener); - password2.addModifyListener(defaultListener); - getManagedForm().addPart(part); - } - - private LdifUsersTable appendMemberOfPart(final Composite parent, User user) { - FormToolkit tk = getManagedForm().getToolkit(); - Section section = addSection(tk, parent, "Roles"); - Composite body = (Composite) section.getClient(); - body.setLayout(EclipseUiUtils.noSpaceGridLayout()); - - boolean isAdmin = UserAdminUtils.isUserInRole(AuthConstants.ROLE_ADMIN); - - // Displayed columns - List columnDefs = new ArrayList(); - columnDefs.add(new ColumnDefinition(new RoleIconLP(), "", 0, 24)); - columnDefs.add(new ColumnDefinition(new CommonNameLP(), "Common Name", - 150)); - columnDefs.add(new ColumnDefinition(new DomainNameLP(), "Domain Name", - 200)); - // Only show technical DN to administrators - if (isAdmin) - columnDefs.add(new ColumnDefinition(new UserNameLP(), - "Distinguished Name", 120)); - - // Create and configure the table - final LdifUsersTable userViewerCmp = new MyUserTableViewer(body, - SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL, user); - - userViewerCmp.setColumnDefinitions(columnDefs); - if (isAdmin) - userViewerCmp.populateWithStaticFilters(false, false); - else - userViewerCmp.populate(true, false); - GridData gd = EclipseUiUtils.fillAll(); - gd.heightHint = 300; - userViewerCmp.setLayoutData(gd); - - // Controllers - TableViewer userViewer = userViewerCmp.getTableViewer(); - userViewer.addDoubleClickListener(new UserTableDefaultDClickListener()); - int operations = DND.DROP_COPY | DND.DROP_MOVE; - Transfer[] tt = new Transfer[] { TextTransfer.getInstance() }; - GroupDropListener dropL = new GroupDropListener(userAdminWrapper, - userViewer, user); - userViewer.addDropSupport(operations, tt, dropL); - - SectionPart part = new SectionPart((Section) body.getParent()) { - - private GroupChangeListener listener; - - @Override - public void initialize(IManagedForm form) { - super.initialize(form); - listener = editor.new GroupChangeListener(parent.getDisplay(), - this); - userAdminWrapper.addListener(listener); - } - - public void commit(boolean onSave) { - super.commit(onSave); - } - - @Override - public void dispose() { - userAdminWrapper.removeListener(listener); - super.dispose(); - } - - @Override - public void refresh() { - userViewerCmp.refresh(); - super.refresh(); - } - }; - getManagedForm().addPart(part); - addRemoveAbitily(part, userViewer, user); - return userViewerCmp; - } - - private class MyUserTableViewer extends LdifUsersTable { - private static final long serialVersionUID = 2653790051461237329L; - - private Button showSystemRoleBtn; - - private final User user; - private final UserFilter userFilter; - - public MyUserTableViewer(Composite parent, int style, User user) { - super(parent, style, true); - this.user = user; - userFilter = new UserFilter(); - userFilter.setShowSystemRole(false); - } - - protected void populateStaticFilters(Composite staticFilterCmp) { - staticFilterCmp.setLayout(new GridLayout()); - showSystemRoleBtn = new Button(staticFilterCmp, SWT.CHECK); - showSystemRoleBtn.setText("Show system roles"); - showSystemRoleBtn.addSelectionListener(new SelectionAdapter() { - private static final long serialVersionUID = -7033424592697691676L; - - @Override - public void widgetSelected(SelectionEvent e) { - userFilter.setShowSystemRole(showSystemRoleBtn - .getSelection()); - refresh(); - } - }); - } - - @Override - protected List listFilteredElements(String filter) { - List users = (List) editor.getFlatGroups(null); - List filteredUsers = new ArrayList(); - if (users.contains(user)) - users.remove(user); - userFilter.setSearchText(filter); - for (User user : users) - if (userFilter.select(null, null, user)) - filteredUsers.add(user); - return filteredUsers; - } - } - - private void addRemoveAbitily(SectionPart sectionPart, - TableViewer userViewer, User user) { - Section section = sectionPart.getSection(); - ToolBarManager toolBarManager = new ToolBarManager(SWT.FLAT); - ToolBar toolbar = toolBarManager.createControl(section); - final Cursor handCursor = new Cursor(section.getDisplay(), - SWT.CURSOR_HAND); - toolbar.setCursor(handCursor); - toolbar.addDisposeListener(new DisposeListener() { - private static final long serialVersionUID = 3882131405820522925L; - - public void widgetDisposed(DisposeEvent e) { - if ((handCursor != null) && (handCursor.isDisposed() == false)) { - handCursor.dispose(); - } - } - }); - - String tooltip = "Remove " + UserAdminUtils.getUsername(user) - + " from the below selected groups"; - Action action = new RemoveMembershipAction(userViewer, user, tooltip, - SecurityAdminImages.ICON_REMOVE_DESC); - toolBarManager.add(action); - toolBarManager.update(true); - section.setTextClient(toolbar); - } - - private class RemoveMembershipAction extends Action { - private static final long serialVersionUID = -1337713097184522588L; - - private final TableViewer userViewer; - private final User user; - - RemoveMembershipAction(TableViewer userViewer, User user, String name, - ImageDescriptor img) { - super(name, img); - this.userViewer = userViewer; - this.user = user; - } - - @Override - public void run() { - ISelection selection = userViewer.getSelection(); - if (selection.isEmpty()) - return; - - @SuppressWarnings("unchecked") - Iterator it = ((IStructuredSelection) selection).iterator(); - List groups = new ArrayList(); - while (it.hasNext()) { - Group currGroup = it.next(); - groups.add(currGroup); - } - - userAdminWrapper.beginTransactionIfNeeded(); - for (Group group : groups) { - group.removeMember(user); - } - userAdminWrapper.commitOrNotifyTransactionStateChange(); - for (Group group : groups) { - userAdminWrapper.notifyListeners(new UserAdminEvent(null, - UserAdminEvent.ROLE_CHANGED, group)); - } - } - } - - /** - * Defines the table as being a potential target to add group memberships - * (roles) to this user - */ - private class GroupDropListener extends ViewerDropAdapter { - private static final long serialVersionUID = 2893468717831451621L; - - private final UserAdminWrapper myUserAdminWrapper; - private final User myUser; - - public GroupDropListener(UserAdminWrapper userAdminWrapper, - Viewer userViewer, User user) { - super(userViewer); - this.myUserAdminWrapper = userAdminWrapper; - this.myUser = user; - } - - @Override - public boolean validateDrop(Object target, int operation, - TransferData transferType) { - // Target is always OK in a list only view - // TODO check if not a string - boolean validDrop = true; - return validDrop; - } - - @Override - public void drop(DropTargetEvent event) { - String name = (String) event.data; - UserAdmin myUserAdmin = myUserAdminWrapper.getUserAdmin(); - Role role = myUserAdmin.getRole(name); - // TODO this check should be done before. - if (role.getType() == Role.GROUP) { - // TODO check if the user is already member of this group - - myUserAdminWrapper.beginTransactionIfNeeded(); - Group group = (Group) role; - group.addMember(myUser); - userAdminWrapper.commitOrNotifyTransactionStateChange(); - myUserAdminWrapper.notifyListeners(new UserAdminEvent(null, - UserAdminEvent.ROLE_CHANGED, group)); - } - super.drop(event); - } - - @Override - public boolean performDrop(Object data) { - // userTableViewerCmp.refresh(); - return true; - } - } - - // LOCAL HELPERS - private void refreshFormTitle(User group) { - getManagedForm().getForm().setText( - UserAdminUtils.getProperty(group, LdifName.cn.name())); - } - - /** Appends a section with a title */ - private Section addSection(FormToolkit tk, Composite parent, String title) { - Section section = tk.createSection(parent, Section.TITLE_BAR); - GridData gd = EclipseUiUtils.fillWidth(); - gd.verticalAlignment = PRE_TITLE_INDENT; - section.setLayoutData(gd); - section.setText(title); - // section.getMenu().setVisible(true); - - Composite body = tk.createComposite(section, SWT.WRAP); - body.setLayoutData(EclipseUiUtils.fillAll()); - section.setClient(body); - - return section; - } - - /** Creates label and multiline text. */ - Text createLMT(FormToolkit toolkit, Composite body, String label, - String value) { - Label lbl = toolkit.createLabel(body, label); - lbl.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false)); - Text text = toolkit.createText(body, value, SWT.BORDER | SWT.MULTI); - text.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, true)); - return text; - } - - /** Creates label and password. */ - Text createLP(FormToolkit toolkit, Composite body, String label, - String value) { - Label lbl = toolkit.createLabel(body, label); - lbl.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false)); - Text text = toolkit.createText(body, value, SWT.BORDER | SWT.PASSWORD); - text.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false)); - return text; - } - - /** Creates label and text. */ - Text createLT(FormToolkit toolkit, Composite body, String label, - String value) { - Label lbl = toolkit.createLabel(body, label); - lbl.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false)); - Text text = toolkit.createText(body, value, SWT.BORDER); - text.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false)); - return text; - } -} \ No newline at end of file diff --git a/org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/internal/parts/UsersView.java b/org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/internal/parts/UsersView.java deleted file mode 100644 index a397432a8..000000000 --- a/org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/internal/parts/UsersView.java +++ /dev/null @@ -1,194 +0,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. - */ -package org.argeo.security.ui.admin.internal.parts; - -import java.util.ArrayList; -import java.util.List; - -import org.argeo.cms.CmsException; -import org.argeo.cms.auth.AuthConstants; -import org.argeo.cms.util.useradmin.UserAdminUtils; -import org.argeo.eclipse.ui.ColumnDefinition; -import org.argeo.eclipse.ui.EclipseUiUtils; -import org.argeo.eclipse.ui.parts.LdifUsersTable; -import org.argeo.jcr.ArgeoNames; -import org.argeo.osgi.useradmin.LdifName; -import org.argeo.security.ui.admin.SecurityAdminPlugin; -import org.argeo.security.ui.admin.internal.UiUserAdminListener; -import org.argeo.security.ui.admin.internal.UserAdminWrapper; -import org.argeo.security.ui.admin.internal.providers.CommonNameLP; -import org.argeo.security.ui.admin.internal.providers.DomainNameLP; -import org.argeo.security.ui.admin.internal.providers.MailLP; -import org.argeo.security.ui.admin.internal.providers.UserDragListener; -import org.argeo.security.ui.admin.internal.providers.UserNameLP; -import org.argeo.security.ui.admin.internal.providers.UserTableDefaultDClickListener; -import org.eclipse.jface.viewers.TableViewer; -import org.eclipse.swt.SWT; -import org.eclipse.swt.dnd.DND; -import org.eclipse.swt.dnd.TextTransfer; -import org.eclipse.swt.dnd.Transfer; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Display; -import org.eclipse.ui.part.ViewPart; -import org.osgi.framework.InvalidSyntaxException; -import org.osgi.service.useradmin.Role; -import org.osgi.service.useradmin.User; -import org.osgi.service.useradmin.UserAdminEvent; -import org.osgi.service.useradmin.UserAdminListener; - -/** List all users with filter - based on Ldif userAdmin */ -public class UsersView extends ViewPart implements ArgeoNames { - // private final static Log log = LogFactory.getLog(UsersView.class); - - public final static String ID = SecurityAdminPlugin.PLUGIN_ID - + ".usersView"; - - /* DEPENDENCY INJECTION */ - private UserAdminWrapper userAdminWrapper; - - // UI Objects - private LdifUsersTable userTableViewerCmp; - private TableViewer userViewer; - private List columnDefs = new ArrayList(); - - private UserAdminListener listener; - - @Override - public void createPartControl(Composite parent) { - - parent.setLayout(EclipseUiUtils.noSpaceGridLayout()); - // Define the displayed columns - columnDefs.add(new ColumnDefinition(new CommonNameLP(), "Common Name", - 150)); - columnDefs.add(new ColumnDefinition(new MailLP(), "E-mail", 150)); - columnDefs.add(new ColumnDefinition(new DomainNameLP(), "Domain", 200)); - // Only show technical DN to admin - if (UserAdminUtils.isUserInRole(AuthConstants.ROLE_ADMIN)) - columnDefs.add(new ColumnDefinition(new UserNameLP(), - "Distinguished Name", 300)); - - // Create and configure the table - userTableViewerCmp = new MyUserTableViewer(parent, SWT.MULTI - | SWT.H_SCROLL | SWT.V_SCROLL); - userTableViewerCmp.setLayoutData(EclipseUiUtils.fillAll()); - userTableViewerCmp.setColumnDefinitions(columnDefs); - userTableViewerCmp.populate(true, false); - - // Links - userViewer = userTableViewerCmp.getTableViewer(); - userViewer.addDoubleClickListener(new UserTableDefaultDClickListener()); - getViewSite().setSelectionProvider(userViewer); - - // Really? - userTableViewerCmp.refresh(); - - // Drag and drop - int operations = DND.DROP_COPY | DND.DROP_MOVE; - Transfer[] tt = new Transfer[] { TextTransfer.getInstance() }; - userViewer.addDragSupport(operations, tt, new UserDragListener( - userViewer)); - - // Register a useradmin listener - listener = new MyUiUAListener(parent.getDisplay()); - userAdminWrapper.addListener(listener); - } - - private class MyUiUAListener extends UiUserAdminListener { - public MyUiUAListener(Display display) { - super(display); - } - - @Override - public void roleChangedToUiThread(UserAdminEvent event) { - if (userViewer != null && !userViewer.getTable().isDisposed()) - refresh(); - } - } - - private class MyUserTableViewer extends LdifUsersTable { - private static final long serialVersionUID = 8467999509931900367L; - - private final String[] knownProps = { LdifName.uid.name(), - LdifName.dn.name(), LdifName.cn.name(), - LdifName.givenName.name(), LdifName.sn.name(), - LdifName.mail.name() }; - - public MyUserTableViewer(Composite parent, int style) { - super(parent, style); - } - - @Override - protected List listFilteredElements(String filter) { - Role[] roles; - - try { - StringBuilder builder = new StringBuilder(); - - StringBuilder tmpBuilder = new StringBuilder(); - if (EclipseUiUtils.notEmpty(filter)) - for (String prop : knownProps) { - tmpBuilder.append("("); - tmpBuilder.append(prop); - tmpBuilder.append("=*"); - tmpBuilder.append(filter); - tmpBuilder.append("*)"); - } - if (tmpBuilder.length() > 1) { - builder.append("(&(").append(LdifName.objectClass.name()) - .append("=").append(LdifName.inetOrgPerson.name()) - .append(")(|"); - builder.append(tmpBuilder.toString()); - builder.append("))"); - } else - builder.append("(").append(LdifName.objectClass.name()) - .append("=").append(LdifName.inetOrgPerson.name()) - .append(")"); - roles = userAdminWrapper.getUserAdmin().getRoles( - builder.toString()); - } catch (InvalidSyntaxException e) { - throw new CmsException("Unable to get roles with filter: " - + filter, e); - } - List users = new ArrayList(); - for (Role role : roles) - // if (role.getType() == Role.USER && role.getType() != - // Role.GROUP) - users.add((User) role); - return users; - } - } - - public void refresh() { - userTableViewerCmp.refresh(); - } - - // Override generic view methods - @Override - public void dispose() { - userAdminWrapper.removeListener(listener); - super.dispose(); - } - - @Override - public void setFocus() { - userTableViewerCmp.setFocus(); - } - - /* DEPENDENCY INJECTION */ - public void setUserAdminWrapper(UserAdminWrapper userAdminWrapper) { - this.userAdminWrapper = userAdminWrapper; - } -} \ No newline at end of file diff --git a/org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/internal/providers/CommonNameLP.java b/org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/internal/providers/CommonNameLP.java deleted file mode 100644 index d45c0b603..000000000 --- a/org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/internal/providers/CommonNameLP.java +++ /dev/null @@ -1,15 +0,0 @@ -package org.argeo.security.ui.admin.internal.providers; - -import org.argeo.cms.util.useradmin.UserAdminUtils; -import org.argeo.osgi.useradmin.LdifName; -import org.osgi.service.useradmin.User; - -/** Simply declare a label provider that returns the common name of a user */ -public class CommonNameLP extends UserAdminAbstractLP { - private static final long serialVersionUID = 5256703081044911941L; - - @Override - public String getText(User user) { - return UserAdminUtils.getProperty(user, LdifName.cn.name()); - } -} \ No newline at end of file diff --git a/org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/internal/providers/DomainNameLP.java b/org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/internal/providers/DomainNameLP.java deleted file mode 100644 index 795fd0af3..000000000 --- a/org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/internal/providers/DomainNameLP.java +++ /dev/null @@ -1,14 +0,0 @@ -package org.argeo.security.ui.admin.internal.providers; - -import org.argeo.cms.util.useradmin.UserAdminUtils; -import org.osgi.service.useradmin.User; - -/** The human friendly domain name for the corresponding user. */ -public class DomainNameLP extends UserAdminAbstractLP { - private static final long serialVersionUID = 5256703081044911941L; - - @Override - public String getText(User user) { - return UserAdminUtils.getDomainName(user); - } -} \ No newline at end of file diff --git a/org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/internal/providers/MailLP.java b/org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/internal/providers/MailLP.java deleted file mode 100644 index 0a6dcb604..000000000 --- a/org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/internal/providers/MailLP.java +++ /dev/null @@ -1,15 +0,0 @@ -package org.argeo.security.ui.admin.internal.providers; - -import org.argeo.cms.util.useradmin.UserAdminUtils; -import org.argeo.osgi.useradmin.LdifName; -import org.osgi.service.useradmin.User; - -/** Simply declare a label provider that returns the Primary Mail of a user */ -public class MailLP extends UserAdminAbstractLP { - private static final long serialVersionUID = 8329764452141982707L; - - @Override - public String getText(User user) { - return UserAdminUtils.getProperty(user, LdifName.mail.name()); - } -} \ No newline at end of file diff --git a/org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/internal/providers/RoleIconLP.java b/org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/internal/providers/RoleIconLP.java deleted file mode 100644 index bb19220a9..000000000 --- a/org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/internal/providers/RoleIconLP.java +++ /dev/null @@ -1,29 +0,0 @@ -package org.argeo.security.ui.admin.internal.providers; - -import org.argeo.cms.auth.AuthConstants; -import org.argeo.security.ui.admin.SecurityAdminImages; -import org.eclipse.swt.graphics.Image; -import org.osgi.service.useradmin.Role; -import org.osgi.service.useradmin.User; - -/** Provide a bundle specific image depending on the current user type */ -public class RoleIconLP extends UserAdminAbstractLP { - private static final long serialVersionUID = 6550449442061090388L; - - @Override - public String getText(User user) { - return ""; - } - - @Override - public Image getImage(Object element) { - User user = (User) element; - String dn = user.getName(); - if (dn.endsWith(AuthConstants.ROLES_BASEDN)) - return SecurityAdminImages.ICON_ROLE; - else if (user.getType() == Role.GROUP) - return SecurityAdminImages.ICON_GROUP; - else - return SecurityAdminImages.ICON_USER; - } -} \ No newline at end of file diff --git a/org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/internal/providers/UserAdminAbstractLP.java b/org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/internal/providers/UserAdminAbstractLP.java deleted file mode 100644 index 2b0a13db7..000000000 --- a/org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/internal/providers/UserAdminAbstractLP.java +++ /dev/null @@ -1,66 +0,0 @@ -package org.argeo.security.ui.admin.internal.providers; - -import javax.naming.InvalidNameException; -import javax.naming.ldap.LdapName; - -import org.argeo.cms.CmsException; -import org.argeo.cms.util.useradmin.UserAdminUtils; -import org.eclipse.jface.resource.JFaceResources; -import org.eclipse.jface.viewers.ColumnLabelProvider; -import org.eclipse.swt.SWT; -import org.eclipse.swt.graphics.Font; -import org.eclipse.swt.widgets.Display; -import org.osgi.service.useradmin.User; - -/** - * Utility class that add font modifications to a column label provider - * depending on the given user properties - */ -public abstract class UserAdminAbstractLP extends ColumnLabelProvider { - private static final long serialVersionUID = 137336765024922368L; - - // private Font italic; - private Font bold; - - @Override - public Font getFont(Object element) { - // Self as bold - try { - LdapName selfUserName = UserAdminUtils.getCurrentUserLdapName(); - String userName = ((User) element).getName(); - LdapName userLdapName = new LdapName(userName); - if (userLdapName.equals(selfUserName)) { - if (bold == null) - bold = JFaceResources.getFontRegistry() - .defaultFontDescriptor().setStyle(SWT.BOLD) - .createFont(Display.getCurrent()); - return bold; - } - } catch (InvalidNameException e) { - throw new CmsException("cannot parse dn for " + element, e); - } - - // Disabled as Italic - // Node userProfile = (Node) elem; - // if (!userProfile.getProperty(ARGEO_ENABLED).getBoolean()) - // return italic; - - return null; - // return super.getFont(element); - } - - @Override - public String getText(Object element) { - User user = (User) element; - return getText(user); - } - - public void setDisplay(Display display) { - // italic = JFaceResources.getFontRegistry().defaultFontDescriptor() - // .setStyle(SWT.ITALIC).createFont(display); - bold = JFaceResources.getFontRegistry().defaultFontDescriptor() - .setStyle(SWT.BOLD).createFont(Display.getCurrent()); - } - - public abstract String getText(User user); -} diff --git a/org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/internal/providers/UserDragListener.java b/org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/internal/providers/UserDragListener.java deleted file mode 100644 index f60b64c65..000000000 --- a/org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/internal/providers/UserDragListener.java +++ /dev/null @@ -1,40 +0,0 @@ -package org.argeo.security.ui.admin.internal.providers; - -import org.eclipse.jface.viewers.IStructuredSelection; -import org.eclipse.jface.viewers.Viewer; -import org.eclipse.swt.dnd.DragSourceEvent; -import org.eclipse.swt.dnd.DragSourceListener; -import org.osgi.service.useradmin.User; - -/** Default drag listener to modify group and users via the UI */ -public class UserDragListener implements DragSourceListener { - private static final long serialVersionUID = -2074337775033781454L; - private final Viewer viewer; - - public UserDragListener(Viewer viewer) { - this.viewer = viewer; - } - - public void dragStart(DragSourceEvent event) { - // TODO implement finer checks - IStructuredSelection selection = (IStructuredSelection) viewer - .getSelection(); - if (selection.isEmpty() || selection.size() > 1) - event.doit = false; - else - event.doit = true; - } - - public void dragSetData(DragSourceEvent event) { - // TODO Support multiple selection - Object obj = ((IStructuredSelection) viewer.getSelection()) - .getFirstElement(); - if (obj != null) { - User user = (User) obj; - event.data = user.getName(); - } - } - - public void dragFinished(DragSourceEvent event) { - } -} \ No newline at end of file diff --git a/org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/internal/providers/UserFilter.java b/org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/internal/providers/UserFilter.java deleted file mode 100644 index 5f753d1c3..000000000 --- a/org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/internal/providers/UserFilter.java +++ /dev/null @@ -1,60 +0,0 @@ -package org.argeo.security.ui.admin.internal.providers; - -import static org.argeo.eclipse.ui.EclipseUiUtils.notEmpty; - -import org.argeo.cms.auth.AuthConstants; -import org.argeo.cms.util.useradmin.UserAdminUtils; -import org.argeo.osgi.useradmin.LdifName; -import org.eclipse.jface.viewers.Viewer; -import org.eclipse.jface.viewers.ViewerFilter; -import org.osgi.service.useradmin.User; - -public class UserFilter extends ViewerFilter { - private static final long serialVersionUID = 5082509381672880568L; - - private String searchString; - private boolean showSystemRole = true; - - private final String[] knownProps = { LdifName.dn.name(), - LdifName.cn.name(), LdifName.givenName.name(), LdifName.sn.name(), - LdifName.uid.name(), LdifName.description.name(), - LdifName.mail.name() }; - - public void setSearchText(String s) { - // ensure that the value can be used for matching - if (notEmpty(s)) - searchString = ".*" + s.toLowerCase() + ".*"; - else - searchString = ".*"; - } - - public void setShowSystemRole(boolean showSystemRole) { - this.showSystemRole = showSystemRole; - } - - @Override - public boolean select(Viewer viewer, Object parentElement, Object element) { - User user = (User) element; - if (!showSystemRole - && user.getName().matches( - ".*(" + AuthConstants.ROLES_BASEDN + ")")) - // UserAdminUtils.getProperty(user, LdifName.dn.name()) - // .toLowerCase().endsWith(AuthConstants.ROLES_BASEDN)) - return false; - - if (searchString == null || searchString.length() == 0) - return true; - - if (user.getName().matches(searchString)) - return true; - - for (String key : knownProps) { - String currVal = UserAdminUtils.getProperty(user, key); - if (notEmpty(currVal) - && currVal.toLowerCase().matches(searchString)) - return true; - } - return false; - } - -} \ No newline at end of file diff --git a/org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/internal/providers/UserNameLP.java b/org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/internal/providers/UserNameLP.java deleted file mode 100644 index a2a15c112..000000000 --- a/org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/internal/providers/UserNameLP.java +++ /dev/null @@ -1,13 +0,0 @@ -package org.argeo.security.ui.admin.internal.providers; - -import org.osgi.service.useradmin.User; - -/** Simply declare a label provider that returns the username of a user */ -public class UserNameLP extends UserAdminAbstractLP { - private static final long serialVersionUID = 6550449442061090388L; - - @Override - public String getText(User user) { - return user.getName(); - } -} diff --git a/org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/internal/providers/UserTableDefaultDClickListener.java b/org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/internal/providers/UserTableDefaultDClickListener.java deleted file mode 100644 index 8f4a35a18..000000000 --- a/org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/internal/providers/UserTableDefaultDClickListener.java +++ /dev/null @@ -1,44 +0,0 @@ -package org.argeo.security.ui.admin.internal.providers; - -import org.argeo.cms.CmsException; -import org.argeo.eclipse.ui.workbench.WorkbenchUiPlugin; -import org.argeo.security.ui.admin.internal.parts.UserEditor; -import org.argeo.security.ui.admin.internal.parts.UserEditorInput; -import org.eclipse.jface.viewers.DoubleClickEvent; -import org.eclipse.jface.viewers.IDoubleClickListener; -import org.eclipse.jface.viewers.IStructuredSelection; -import org.eclipse.ui.IWorkbenchPage; -import org.eclipse.ui.IWorkbenchWindow; -import org.eclipse.ui.PartInitException; -import org.osgi.service.useradmin.Group; -import org.osgi.service.useradmin.User; - -/** - * Default double click listener for the various user tables, will open the - * clicked item in the editor - */ -public class UserTableDefaultDClickListener implements IDoubleClickListener { - public void doubleClick(DoubleClickEvent evt) { - if (evt.getSelection().isEmpty()) - return; - Object obj = ((IStructuredSelection) evt.getSelection()) - .getFirstElement(); - User user = (User) obj; - IWorkbenchWindow iww = WorkbenchUiPlugin.getDefault().getWorkbench() - .getActiveWorkbenchWindow(); - IWorkbenchPage iwp = iww.getActivePage(); - UserEditorInput uei = new UserEditorInput(user.getName()); - - try { - // Works around the fact that dynamic setting of the editor icon - // causes NPE after a login/logout on RAP - if (user instanceof Group) - iwp.openEditor(uei, UserEditor.GROUP_EDITOR_ID); - else - iwp.openEditor(uei, UserEditor.USER_EDITOR_ID); - } catch (PartInitException pie) { - throw new CmsException("Unable to open UserEditor for " + user, - pie); - } - } -} \ No newline at end of file diff --git a/org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/internal/providers/UserTransactionProvider.java b/org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/internal/providers/UserTransactionProvider.java deleted file mode 100644 index 4ba304bb5..000000000 --- a/org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/internal/providers/UserTransactionProvider.java +++ /dev/null @@ -1,74 +0,0 @@ -package org.argeo.security.ui.admin.internal.providers; - -import java.util.HashMap; -import java.util.Map; - -import javax.transaction.Status; -import javax.transaction.UserTransaction; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.argeo.cms.CmsException; -import org.argeo.security.ui.admin.SecurityAdminPlugin; -import org.eclipse.ui.AbstractSourceProvider; -import org.eclipse.ui.ISources; - -/** Observe and notify UI on UserTransaction state changes */ -public class UserTransactionProvider extends AbstractSourceProvider { - private final static Log log = LogFactory - .getLog(UserTransactionProvider.class); - - public final static String TRANSACTION_STATE = SecurityAdminPlugin.PLUGIN_ID - + ".userTransactionState"; - public final static String STATUS_ACTIVE = "status.active"; - public final static String STATUS_NO_TRANSACTION = "status.noTransaction"; - - /* DEPENDENCY INJECTION */ - private UserTransaction userTransaction; - - @Override - public String[] getProvidedSourceNames() { - return new String[] { TRANSACTION_STATE}; - } - - @Override - public Map getCurrentState() { - Map currentState = new HashMap(1); - currentState.put(TRANSACTION_STATE, getInternalCurrentState()); - return currentState; - } - - @Override - public void dispose() { - } - - private String getInternalCurrentState() { - try { - String transactionState; - if (userTransaction.getStatus() == Status.STATUS_NO_TRANSACTION) - transactionState = STATUS_NO_TRANSACTION; - else - // if (userTransaction.getStatus() == Status.STATUS_ACTIVE) - transactionState = STATUS_ACTIVE; - return transactionState; - } catch (Exception e) { - throw new CmsException("Unable to begin transaction", e); - } - } - - /** Publishes the ability to notify a state change */ - public void fireTransactionStateChange() { - try { - fireSourceChanged(ISources.WORKBENCH, TRANSACTION_STATE, - getInternalCurrentState()); - } catch (Exception e) { - log.warn("Cannot fire transaction state change event. Caught exception: " - + e.getClass().getCanonicalName() + " - " + e.getMessage()); - } - } - - /* DEPENDENCY INJECTION */ - public void setUserTransaction(UserTransaction userTransaction) { - this.userTransaction = userTransaction; - } -} \ No newline at end of file diff --git a/org.argeo.security.ui.rap/.classpath b/org.argeo.security.ui.rap/.classpath deleted file mode 100644 index 457b11571..000000000 --- a/org.argeo.security.ui.rap/.classpath +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - diff --git a/org.argeo.security.ui.rap/.project b/org.argeo.security.ui.rap/.project deleted file mode 100644 index d20bec7fc..000000000 --- a/org.argeo.security.ui.rap/.project +++ /dev/null @@ -1,28 +0,0 @@ - - - org.argeo.security.ui.rap - - - - - - org.eclipse.jdt.core.javabuilder - - - - - org.eclipse.pde.ManifestBuilder - - - - - org.eclipse.pde.SchemaBuilder - - - - - - org.eclipse.pde.PluginNature - org.eclipse.jdt.core.javanature - - diff --git a/org.argeo.security.ui.rap/META-INF/spring/commands.xml b/org.argeo.security.ui.rap/META-INF/spring/commands.xml deleted file mode 100644 index 802d31de6..000000000 --- a/org.argeo.security.ui.rap/META-INF/spring/commands.xml +++ /dev/null @@ -1,23 +0,0 @@ - - - - - - - - - - - - - - - - diff --git a/org.argeo.security.ui.rap/META-INF/spring/osgi.xml b/org.argeo.security.ui.rap/META-INF/spring/osgi.xml deleted file mode 100644 index 84e5d7b8b..000000000 --- a/org.argeo.security.ui.rap/META-INF/spring/osgi.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/org.argeo.security.ui.rap/OSGI-INF/l10n/bundle.properties b/org.argeo.security.ui.rap/OSGI-INF/l10n/bundle.properties deleted file mode 100644 index 4dff7af99..000000000 --- a/org.argeo.security.ui.rap/OSGI-INF/l10n/bundle.properties +++ /dev/null @@ -1 +0,0 @@ -changePassword=Change password \ No newline at end of file diff --git a/org.argeo.security.ui.rap/OSGI-INF/l10n/bundle_fr.properties b/org.argeo.security.ui.rap/OSGI-INF/l10n/bundle_fr.properties deleted file mode 100644 index 158d6faa0..000000000 --- a/org.argeo.security.ui.rap/OSGI-INF/l10n/bundle_fr.properties +++ /dev/null @@ -1 +0,0 @@ -changePassword=Changer de mot de passe \ No newline at end of file diff --git a/org.argeo.security.ui.rap/OSGI-INF/l10n/bundle_ru.properties b/org.argeo.security.ui.rap/OSGI-INF/l10n/bundle_ru.properties deleted file mode 100644 index 11dd1007c..000000000 --- a/org.argeo.security.ui.rap/OSGI-INF/l10n/bundle_ru.properties +++ /dev/null @@ -1 +0,0 @@ -changePassword=\u0418\u0437\u043C\u0435\u043D\u0438\u0442\u044C \u043F\u0430\u0440\u043E\u043B\u044C diff --git a/org.argeo.security.ui.rap/bnd.bnd b/org.argeo.security.ui.rap/bnd.bnd deleted file mode 100644 index 1da6c05d5..000000000 --- a/org.argeo.security.ui.rap/bnd.bnd +++ /dev/null @@ -1,12 +0,0 @@ -Bundle-SymbolicName: org.argeo.security.ui.rap;singleton:=true -Bundle-Activator: org.argeo.security.ui.rap.SecureRapActivator -Bundle-ActivationPolicy: lazy -Require-Bundle: org.eclipse.rap.ui,org.eclipse.core.runtime - -Import-Package: org.argeo.eclipse.spring,\ -org.argeo.eclipse.ui.specific,\ -org.argeo.eclipse.ui.workbench.commands,\ -org.argeo.cms,\ -org.argeo.cms.auth,\ -org.argeo.security.ui,\ -* diff --git a/org.argeo.security.ui.rap/branding/afterLogout.html b/org.argeo.security.ui.rap/branding/afterLogout.html deleted file mode 100644 index ae0901bff..000000000 --- a/org.argeo.security.ui.rap/branding/afterLogout.html +++ /dev/null @@ -1,18 +0,0 @@ - - - -
- - - - -
- Login... -
-
- - \ No newline at end of file diff --git a/org.argeo.security.ui.rap/branding/empty.html b/org.argeo.security.ui.rap/branding/empty.html deleted file mode 100644 index 94fe28ac8..000000000 --- a/org.argeo.security.ui.rap/branding/empty.html +++ /dev/null @@ -1,5 +0,0 @@ - - - - - \ No newline at end of file diff --git a/org.argeo.security.ui.rap/branding/favicon.ico b/org.argeo.security.ui.rap/branding/favicon.ico deleted file mode 100644 index 213cdf73f..000000000 Binary files a/org.argeo.security.ui.rap/branding/favicon.ico and /dev/null differ diff --git a/org.argeo.security.ui.rap/branding/login.html b/org.argeo.security.ui.rap/branding/login.html deleted file mode 100644 index 6de7eb216..000000000 --- a/org.argeo.security.ui.rap/branding/login.html +++ /dev/null @@ -1,18 +0,0 @@ - - - -
- - - - -
- Login... -
-
- - \ No newline at end of file diff --git a/org.argeo.security.ui.rap/branding/public.html b/org.argeo.security.ui.rap/branding/public.html deleted file mode 100644 index e50f6e943..000000000 --- a/org.argeo.security.ui.rap/branding/public.html +++ /dev/null @@ -1,18 +0,0 @@ - - - -
- - - - -
- Refresh... -
-
- - \ No newline at end of file diff --git a/org.argeo.security.ui.rap/build.properties b/org.argeo.security.ui.rap/build.properties deleted file mode 100644 index ae37429d8..000000000 --- a/org.argeo.security.ui.rap/build.properties +++ /dev/null @@ -1,2 +0,0 @@ -source.. = src/ -bin.includes = OSGI-INF/ diff --git a/org.argeo.security.ui.rap/icons/closeAll.gif b/org.argeo.security.ui.rap/icons/closeAll.gif deleted file mode 100644 index 28a3785aa..000000000 Binary files a/org.argeo.security.ui.rap/icons/closeAll.gif and /dev/null differ diff --git a/org.argeo.security.ui.rap/icons/exit.png b/org.argeo.security.ui.rap/icons/exit.png deleted file mode 100644 index cfbf9d15e..000000000 Binary files a/org.argeo.security.ui.rap/icons/exit.png and /dev/null differ diff --git a/org.argeo.security.ui.rap/icons/home.gif b/org.argeo.security.ui.rap/icons/home.gif deleted file mode 100644 index fd0c66950..000000000 Binary files a/org.argeo.security.ui.rap/icons/home.gif and /dev/null differ diff --git a/org.argeo.security.ui.rap/icons/main.gif b/org.argeo.security.ui.rap/icons/main.gif deleted file mode 100644 index 90a00147b..000000000 Binary files a/org.argeo.security.ui.rap/icons/main.gif and /dev/null differ diff --git a/org.argeo.security.ui.rap/icons/password.gif b/org.argeo.security.ui.rap/icons/password.gif deleted file mode 100644 index a6b251fc8..000000000 Binary files a/org.argeo.security.ui.rap/icons/password.gif and /dev/null differ diff --git a/org.argeo.security.ui.rap/icons/preferences.png b/org.argeo.security.ui.rap/icons/preferences.png deleted file mode 100644 index aa0dc0be9..000000000 Binary files a/org.argeo.security.ui.rap/icons/preferences.png and /dev/null differ diff --git a/org.argeo.security.ui.rap/plugin.xml b/org.argeo.security.ui.rap/plugin.xml deleted file mode 100644 index 243a433d0..000000000 --- a/org.argeo.security.ui.rap/plugin.xml +++ /dev/null @@ -1,179 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/org.argeo.security.ui.rap/pom.xml b/org.argeo.security.ui.rap/pom.xml deleted file mode 100644 index 5f8c9943d..000000000 --- a/org.argeo.security.ui.rap/pom.xml +++ /dev/null @@ -1,35 +0,0 @@ - - - 4.0.0 - - org.argeo.commons - 2.1.46-SNAPSHOT - argeo-commons - .. - - org.argeo.security.ui.rap - Commons CMS Workbench RAP - jar - - - org.argeo.commons - org.argeo.util - 2.1.46-SNAPSHOT - - - org.argeo.commons - org.argeo.security.ui - 2.1.46-SNAPSHOT - - - org.argeo.commons - org.argeo.eclipse.ui.rap - 2.1.46-SNAPSHOT - - - org.argeo.commons - org.argeo.cms - 2.1.46-SNAPSHOT - - - \ No newline at end of file diff --git a/org.argeo.security.ui.rap/src/org/argeo/security/ui/rap/AnonymousEntryPoint.java b/org.argeo.security.ui.rap/src/org/argeo/security/ui/rap/AnonymousEntryPoint.java deleted file mode 100644 index 04b6f0a9d..000000000 --- a/org.argeo.security.ui.rap/src/org/argeo/security/ui/rap/AnonymousEntryPoint.java +++ /dev/null @@ -1,112 +0,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. - */ -package org.argeo.security.ui.rap; - -import java.security.PrivilegedAction; - -import javax.security.auth.Subject; -import javax.security.auth.login.LoginContext; -import javax.security.auth.login.LoginException; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.argeo.cms.CmsException; -import org.argeo.cms.auth.AuthConstants; -import org.eclipse.rap.rwt.RWT; -import org.eclipse.rap.rwt.application.EntryPoint; -import org.eclipse.swt.widgets.Display; -import org.eclipse.ui.PlatformUI; - -/** - * RAP entry point which authenticates the subject as anonymous, for public - * unauthenticated access. - */ -public class AnonymousEntryPoint implements EntryPoint { - private final static Log log = LogFactory.getLog(AnonymousEntryPoint.class); - - /** - * How many seconds to wait before invalidating the session if the user has - * not yet logged in. - */ - private Integer sessionTimeout = 5 * 60; - - @Override - public int createUI() { - RWT.getRequest().getSession().setMaxInactiveInterval(sessionTimeout); - - // if (log.isDebugEnabled()) - // log.debug("Anonymous THREAD=" + Thread.currentThread().getId() - // + ", sessionStore=" + RWT.getSessionStore().getId()); - - final Display display = PlatformUI.createDisplay(); - Subject subject = new Subject(); - - final LoginContext loginContext; - try { - loginContext = new LoginContext(AuthConstants.LOGIN_CONTEXT_ANONYMOUS, - subject); - loginContext.login(); - } catch (LoginException e1) { - throw new CmsException("Cannot initialize login context", e1); - } - - // identify after successful login - if (log.isDebugEnabled()) - log.debug("Authenticated " + subject); - final String username = subject.getPrincipals().iterator().next() - .getName(); - - // Logout callback when the display is disposed - display.disposeExec(new Runnable() { - public void run() { - log.debug("Display disposed"); - logout(loginContext, username); - } - }); - - // - // RUN THE WORKBENCH - // - Integer returnCode = null; - try { - returnCode = Subject.doAs(subject, new PrivilegedAction() { - public Integer run() { - RapWorkbenchAdvisor workbenchAdvisor = new RapWorkbenchAdvisor( - null); - int result = PlatformUI.createAndRunWorkbench(display, - workbenchAdvisor); - return new Integer(result); - } - }); - logout(loginContext, username); - if (log.isTraceEnabled()) - log.trace("Return code " + returnCode); - } finally { - display.dispose(); - } - return 1; - } - - private void logout(LoginContext loginContext, String username) { - try { - loginContext.logout(); - log.info("Logged out " + (username != null ? username : "") - + " (THREAD=" + Thread.currentThread().getId() + ")"); - } catch (LoginException e) { - log.error("Erorr when logging out", e); - } - } -} diff --git a/org.argeo.security.ui.rap/src/org/argeo/security/ui/rap/RapActionBarAdvisor.java b/org.argeo.security.ui.rap/src/org/argeo/security/ui/rap/RapActionBarAdvisor.java deleted file mode 100644 index 14bcee124..000000000 --- a/org.argeo.security.ui.rap/src/org/argeo/security/ui/rap/RapActionBarAdvisor.java +++ /dev/null @@ -1,232 +0,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. - */ -package org.argeo.security.ui.rap; - -import org.argeo.cms.auth.CurrentUser; -import org.argeo.security.ui.commands.OpenHomePerspective; -import org.eclipse.core.commands.Category; -import org.eclipse.core.commands.Command; -import org.eclipse.jface.action.ICoolBarManager; -import org.eclipse.jface.action.IMenuManager; -import org.eclipse.jface.action.IToolBarManager; -import org.eclipse.jface.action.ToolBarManager; -import org.eclipse.swt.SWT; -import org.eclipse.ui.IWorkbenchWindow; -import org.eclipse.ui.actions.ActionFactory; -import org.eclipse.ui.actions.ActionFactory.IWorkbenchAction; -import org.eclipse.ui.application.ActionBarAdvisor; -import org.eclipse.ui.application.IActionBarConfigurer; -import org.eclipse.ui.commands.ICommandService; - -/** Eclipse rap specific action bar advisor */ -public class RapActionBarAdvisor extends ActionBarAdvisor { - private final static String ID_BASE = "org.argeo.security.ui.rap"; - // private final static Log log = LogFactory - // .getLog(SecureActionBarAdvisor.class); - - /** Null means anonymous */ - private String username = null; - - // private IAction logoutAction; - // private IWorkbenchAction openPerspectiveDialogAction; - // private IWorkbenchAction showViewMenuAction; - // private IWorkbenchAction preferences; - private IWorkbenchAction saveAction; - private IWorkbenchAction saveAllAction; - - // private IWorkbenchAction closeAllAction; - - public RapActionBarAdvisor(IActionBarConfigurer configurer, String username) { - super(configurer); - this.username = username; - } - - protected void makeActions(IWorkbenchWindow window) { - // preferences = ActionFactory.PREFERENCES.create(window); - // register(preferences); - // openPerspectiveDialogAction = ActionFactory.OPEN_PERSPECTIVE_DIALOG - // .create(window); - // register(openPerspectiveDialogAction); - // showViewMenuAction = ActionFactory.SHOW_VIEW_MENU.create(window); - // register(showViewMenuAction); - // - // // logout - // logoutAction = ActionFactory.QUIT.create(window); - // // logoutAction = createLogoutAction(); - // register(logoutAction); - // - // Save semantics - saveAction = ActionFactory.SAVE.create(window); - register(saveAction); - saveAllAction = ActionFactory.SAVE_ALL.create(window); - register(saveAllAction); - // closeAllAction = ActionFactory.CLOSE_ALL.create(window); - // register(closeAllAction); - - } - - protected void fillMenuBar(IMenuManager menuBar) { - // MenuManager fileMenu = new MenuManager("&File", - // IWorkbenchActionConstants.M_FILE); - // MenuManager editMenu = new MenuManager("&Edit", - // IWorkbenchActionConstants.M_EDIT); - // MenuManager windowMenu = new MenuManager("&Window", - // IWorkbenchActionConstants.M_WINDOW); - // - // menuBar.add(fileMenu); - // menuBar.add(editMenu); - // menuBar.add(windowMenu); - // // Add a group marker indicating where action set menus will appear. - // menuBar.add(new GroupMarker(IWorkbenchActionConstants.MB_ADDITIONS)); - // - // // File - // fileMenu.add(saveAction); - // fileMenu.add(saveAllAction); - // fileMenu.add(closeAllAction); - // fileMenu.add(new - // GroupMarker(IWorkbenchActionConstants.MB_ADDITIONS)); - // fileMenu.add(new Separator()); - // fileMenu.add(logoutAction); - // - // // Edit - // editMenu.add(preferences); - // - // // Window - // windowMenu.add(openPerspectiveDialogAction); - // windowMenu.add(showViewMenuAction); - } - - @Override - protected void fillCoolBar(ICoolBarManager coolBar) { - // Add a command which label is the display name of the current - // logged-in user - if (username != null) { - ICommandService cmdService = (ICommandService) getActionBarConfigurer() - .getWindowConfigurer().getWorkbenchConfigurer() - .getWorkbench().getService(ICommandService.class); - Category userMenus = cmdService.getCategory(ID_BASE + ".userMenus"); - if (!userMenus.isDefined()) - userMenus.define("User Menus", "User related menus"); - Command userMenu = cmdService.getCommand(ID_BASE - + ".userMenuCommand"); - if (userMenu.isDefined()) - userMenu.undefine(); - userMenu.define(CurrentUser.getDisplayName(), "User menu actions", - userMenus); - // userMenu.define(username, "User menu actions", userMenus); - - userMenu.setHandler(new OpenHomePerspective()); - - // userToolbar.add(new UserMenuAction()); - // coolBar.add(userToolbar); - } else {// anonymous - IToolBarManager userToolbar = new ToolBarManager(SWT.FLAT - | SWT.RIGHT); - // userToolbar.add(logoutAction); - coolBar.add(userToolbar); - } - // IToolBarManager saveToolbar = new ToolBarManager(SWT.FLAT | - // SWT.RIGHT); - // saveToolbar.add(saveAction); - // saveToolbar.add(saveAllAction); - // coolBar.add(saveToolbar); - } - - // class UserMenuAction extends Action implements IWorkbenchAction { - // - // public UserMenuAction() { - // super(username, IAction.AS_DROP_DOWN_MENU); - // // setMenuCreator(new UserMenu()); - // } - // - // @Override - // public String getId() { - // return "org.argeo.security.ui.rap.userMenu"; - // } - // - // @Override - // public void dispose() { - // } - // - // } - - // class UserMenu implements IMenuCreator { - // private Menu menu; - // - // public Menu getMenu(Control parent) { - // Menu menu = new Menu(parent); - // addActionToMenu(menu, logoutAction); - // return menu; - // } - // - // private void addActionToMenu(Menu menu, IAction action) { - // ActionContributionItem item = new ActionContributionItem(action); - // item.fill(menu, -1); - // } - // - // public void dispose() { - // if (menu != null) { - // menu.dispose(); - // } - // } - // - // public Menu getMenu(Menu parent) { - // // Not use - // return null; - // } - // - // } - - // protected IAction createLogoutAction() { - // Subject subject = Subject.getSubject(AccessController.getContext()); - // final String username = subject.getPrincipals().iterator().next() - // .getName(); - // - // IAction logoutAction = new Action() { - // public String getId() { - // return SecureRapActivator.ID + ".logoutAction"; - // } - // - // public String getText() { - // return "Logout " + username; - // } - // - // public void run() { - // // try { - // // Subject subject = SecureRapActivator.getLoginContext() - // // .getSubject(); - // // String subjectStr = subject.toString(); - // // subject.getPrincipals().clear(); - // // SecureRapActivator.getLoginContext().logout(); - // // log.info(subjectStr + " logged out"); - // // } catch (LoginException e) { - // // log.error("Error when logging out", e); - // // } - // // SecureEntryPoint.logout(username); - // // PlatformUI.getWorkbench().close(); - // // try { - // // RWT.getRequest().getSession().setMaxInactiveInterval(1); - // // } catch (Exception e) { - // // if (log.isTraceEnabled()) - // // log.trace("Error when invalidating session", e); - // // } - // } - // - // }; - // return logoutAction; - // } - -} diff --git a/org.argeo.security.ui.rap/src/org/argeo/security/ui/rap/RapWindowAdvisor.java b/org.argeo.security.ui.rap/src/org/argeo/security/ui/rap/RapWindowAdvisor.java deleted file mode 100644 index 05f4787f0..000000000 --- a/org.argeo.security.ui.rap/src/org/argeo/security/ui/rap/RapWindowAdvisor.java +++ /dev/null @@ -1,100 +0,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. - */ -package org.argeo.security.ui.rap; - -import org.eclipse.swt.SWT; -import org.eclipse.swt.graphics.Point; -import org.eclipse.swt.graphics.Rectangle; -import org.eclipse.swt.widgets.Display; -import org.eclipse.swt.widgets.Event; -import org.eclipse.swt.widgets.Listener; -import org.eclipse.swt.widgets.Shell; -import org.eclipse.ui.IWorkbenchWindow; -import org.eclipse.ui.actions.ActionFactory; -import org.eclipse.ui.actions.ActionFactory.IWorkbenchAction; -import org.eclipse.ui.application.ActionBarAdvisor; -import org.eclipse.ui.application.IActionBarConfigurer; -import org.eclipse.ui.application.IWorkbenchWindowConfigurer; -import org.eclipse.ui.application.WorkbenchWindowAdvisor; - -/** Eclipse RAP specific window advisor */ -public class RapWindowAdvisor extends WorkbenchWindowAdvisor { - - private String username; - - public RapWindowAdvisor(IWorkbenchWindowConfigurer configurer, - String username) { - super(configurer); - this.username = username; - } - - @Override - public ActionBarAdvisor createActionBarAdvisor( - IActionBarConfigurer configurer) { - return new RapActionBarAdvisor(configurer, username); - } - - public void preWindowOpen() { - IWorkbenchWindowConfigurer configurer = getWindowConfigurer(); - configurer.setShowCoolBar(true); - configurer.setShowMenuBar(false); - configurer.setShowStatusLine(false); - configurer.setShowPerspectiveBar(true); - configurer.setTitle("Argeo Web UI"); //$NON-NLS-1$ - // Full screen, see - // http://wiki.eclipse.org/RAP/FAQ#How_to_create_a_fullscreen_application - configurer.setShellStyle(SWT.NO_TRIM); - Rectangle bounds = Display.getCurrent().getBounds(); - configurer.setInitialSize(new Point(bounds.width, bounds.height)); - - // Handle window resize in Rap 2.1+ see - // https://bugs.eclipse.org/bugs/show_bug.cgi?id=417254 - Display.getCurrent().addListener(SWT.Resize, new Listener() { - private static final long serialVersionUID = 2970912561866704526L; - - @Override - public void handleEvent(Event event) { - Rectangle bounds = event.display.getBounds(); - IWorkbenchWindow iww = getWindowConfigurer().getWindow() - .getWorkbench().getActiveWorkbenchWindow(); - iww.getShell().setBounds(bounds); - } - }); - } - - @Override - public void postWindowCreate() { - Shell shell = getWindowConfigurer().getWindow().getShell(); - shell.setMaximized(true); - } - - @Override - public void postWindowOpen() { - String defaultPerspective = getWindowConfigurer() - .getWorkbenchConfigurer().getWorkbench() - .getPerspectiveRegistry().getDefaultPerspective(); - if (defaultPerspective == null) { - IWorkbenchWindow window = getWindowConfigurer().getWindow(); - if (window == null) - return; - - IWorkbenchAction openPerspectiveDialogAction = ActionFactory.OPEN_PERSPECTIVE_DIALOG - .create(window); - openPerspectiveDialogAction.run(); - } - } - -} diff --git a/org.argeo.security.ui.rap/src/org/argeo/security/ui/rap/RapWorkbenchAdvisor.java b/org.argeo.security.ui.rap/src/org/argeo/security/ui/rap/RapWorkbenchAdvisor.java deleted file mode 100644 index edde41f14..000000000 --- a/org.argeo.security.ui.rap/src/org/argeo/security/ui/rap/RapWorkbenchAdvisor.java +++ /dev/null @@ -1,64 +0,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. - */ -package org.argeo.security.ui.rap; - -import org.eclipse.ui.IPerspectiveDescriptor; -import org.eclipse.ui.application.IWorkbenchConfigurer; -import org.eclipse.ui.application.IWorkbenchWindowConfigurer; -import org.eclipse.ui.application.WorkbenchAdvisor; -import org.eclipse.ui.application.WorkbenchWindowAdvisor; - -/** Eclipse RAP specific workbench advisor */ -public class RapWorkbenchAdvisor extends WorkbenchAdvisor { - public final static String INITIAL_PERSPECTIVE_PROPERTY = "org.argeo.security.ui.initialPerspective"; - public final static String SAVE_AND_RESTORE_PROPERTY = "org.argeo.security.ui.saveAndRestore"; - - private String initialPerspective = System.getProperty( - INITIAL_PERSPECTIVE_PROPERTY, null); - - private String username; - - public RapWorkbenchAdvisor(String username) { - this.username = username; - } - - @Override - public void initialize(IWorkbenchConfigurer configurer) { - super.initialize(configurer); - Boolean saveAndRestore = Boolean.parseBoolean(System.getProperty( - SAVE_AND_RESTORE_PROPERTY, "false")); - configurer.setSaveAndRestore(saveAndRestore); - } - - public WorkbenchWindowAdvisor createWorkbenchWindowAdvisor( - IWorkbenchWindowConfigurer configurer) { - return new RapWindowAdvisor(configurer, username); - } - - public String getInitialWindowPerspectiveId() { - if (initialPerspective != null) { - // check whether this user can see the declared perspective - // (typically the perspective won't be listed if this user doesn't - // have the right to see it) - IPerspectiveDescriptor pd = getWorkbenchConfigurer().getWorkbench() - .getPerspectiveRegistry() - .findPerspectiveWithId(initialPerspective); - if (pd == null) - return null; - } - return initialPerspective; - } -} diff --git a/org.argeo.security.ui.rap/src/org/argeo/security/ui/rap/RapWorkbenchLogin.java b/org.argeo.security.ui.rap/src/org/argeo/security/ui/rap/RapWorkbenchLogin.java deleted file mode 100644 index 7bf487a88..000000000 --- a/org.argeo.security.ui.rap/src/org/argeo/security/ui/rap/RapWorkbenchLogin.java +++ /dev/null @@ -1,93 +0,0 @@ -package org.argeo.security.ui.rap; - -import java.security.PrivilegedAction; -import java.util.Locale; - -import javax.security.auth.Subject; -import javax.security.auth.x500.X500Principal; - -import org.argeo.cms.CmsMsg; -import org.argeo.cms.auth.CurrentUser; -import org.argeo.cms.util.CmsUtils; -import org.argeo.cms.util.LoginEntryPoint; -import org.eclipse.rap.rwt.RWT; -import org.eclipse.rap.rwt.client.service.JavaScriptExecutor; -import org.eclipse.swt.SWT; -import org.eclipse.swt.events.SelectionListener; -import org.eclipse.swt.widgets.Button; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Display; -import org.eclipse.ui.PlatformUI; - -public class RapWorkbenchLogin extends LoginEntryPoint { - // private final static Log log = - // LogFactory.getLog(RapWorkbenchLogin.class); - - /** Override to provide an application specific workbench advisor */ - protected RapWorkbenchAdvisor createRapWorkbenchAdvisor(String username) { - return new RapWorkbenchAdvisor(username); - } - - @Override - public int createUI() { - JavaScriptExecutor jsExecutor = RWT.getClient().getService( - JavaScriptExecutor.class); - int returnCode; - try { - returnCode = super.createUI(); - } finally { - // always reload - jsExecutor.execute("location.reload()"); - } - return returnCode; - } - - @Override - protected int postLogin() { - final Display display = Display.getCurrent(); - Subject subject = getSubject(); - if (subject.getPrincipals(X500Principal.class).isEmpty()) { - RWT.getClient().getService(JavaScriptExecutor.class) - .execute("location.reload()"); - } - // - // RUN THE WORKBENCH - // - Integer returnCode = null; - try { - returnCode = Subject.doAs(getSubject(), - new PrivilegedAction() { - public Integer run() { - int result = createAndRunWorkbench(display, - CurrentUser.getUsername(getSubject())); - return new Integer(result); - } - }); - // explicit workbench closing - logout(); - } finally { - display.dispose(); - } - return returnCode; - } - - protected int createAndRunWorkbench(Display display, String username) { - RapWorkbenchAdvisor workbenchAdvisor = createRapWorkbenchAdvisor(username); - return PlatformUI.createAndRunWorkbench(display, workbenchAdvisor); - } - - @Override - protected void extendsCredentialsBlock(Composite credentialsBlock, - Locale selectedLocale, SelectionListener loginSelectionListener) { - Button loginButton = new Button(credentialsBlock, SWT.PUSH); - loginButton.setText(CmsMsg.login.lead(selectedLocale)); - loginButton.setLayoutData(CmsUtils.fillWidth()); - loginButton.addSelectionListener(loginSelectionListener); - } - - @Override - protected Display createDisplay() { - return PlatformUI.createDisplay(); - } - -} diff --git a/org.argeo.security.ui.rap/src/org/argeo/security/ui/rap/SecureEntryPoint.java b/org.argeo.security.ui.rap/src/org/argeo/security/ui/rap/SecureEntryPoint.java deleted file mode 100644 index a681527f7..000000000 --- a/org.argeo.security.ui.rap/src/org/argeo/security/ui/rap/SecureEntryPoint.java +++ /dev/null @@ -1,228 +0,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. - */ -package org.argeo.security.ui.rap; - -import java.security.AccessControlContext; -import java.security.AccessController; -import java.security.PrivilegedAction; - -import javax.security.auth.Subject; -import javax.security.auth.login.CredentialNotFoundException; -import javax.security.auth.login.FailedLoginException; -import javax.security.auth.login.LoginContext; -import javax.security.auth.login.LoginException; -import javax.security.auth.x500.X500Principal; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpSession; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.argeo.cms.CmsException; -import org.argeo.cms.auth.AuthConstants; -import org.argeo.cms.auth.ThreadDeathLoginException; -import org.argeo.cms.widgets.auth.DefaultLoginDialog; -import org.argeo.eclipse.ui.dialogs.ErrorFeedback; -import org.eclipse.jface.dialogs.MessageDialog; -import org.eclipse.rap.rwt.RWT; -import org.eclipse.rap.rwt.application.EntryPoint; -import org.eclipse.swt.widgets.Display; -import org.eclipse.ui.PlatformUI; - -/** - * RAP entry point with login capabilities. Once the user has been - * authenticated, the workbench is run as a privileged action by the related - * subject. - */ -@Deprecated -public class SecureEntryPoint implements EntryPoint { - final static String ACCESS_CONTROL_CONTEXT = "org.argeo.node.accessControlContext"; - private final static Log log = LogFactory.getLog(SecureEntryPoint.class); - - /** - * How many seconds to wait before invalidating the session if the user has - * not yet logged in. - */ - private Integer loginTimeout = 1 * 60; - // TODO make it configurable - /** Default session timeout is 8 hours (European working day length) */ - private Integer sessionTimeout = 8 * 60 * 60; - - /** Override to provide an application specific workbench advisor */ - protected RapWorkbenchAdvisor createRapWorkbenchAdvisor(String username) { - return new RapWorkbenchAdvisor(username); - } - - @Override - public final int createUI() { - // Short login timeout so that the modal dialog login doesn't hang - // around too long - RWT.getRequest().getSession().setMaxInactiveInterval(loginTimeout); - - final Display display = PlatformUI.createDisplay(); - - // load context from session - HttpServletRequest httpRequest = RWT.getRequest(); - final HttpSession httpSession = httpRequest.getSession(); - AccessControlContext acc = (AccessControlContext) httpSession - .getAttribute(ACCESS_CONTROL_CONTEXT); - - final Subject subject; - if (acc != null - && Subject.getSubject(acc).getPrincipals(X500Principal.class) - .size() == 1) { - subject = Subject.getSubject(acc); - } else { - subject = new Subject(); - - final LoginContext loginContext; - DefaultLoginDialog callbackHandler; - try { - callbackHandler = new DefaultLoginDialog( - display.getActiveShell()); - loginContext = new LoginContext( - AuthConstants.LOGIN_CONTEXT_USER, subject, - callbackHandler); - } catch (LoginException e1) { - throw new CmsException("Cannot initialize login context", e1); - } - - tryLogin: while (subject.getPrincipals(X500Principal.class).size() == 0) { - try { - loginContext.login(); - if (subject.getPrincipals(X500Principal.class).size() == 0) - throw new CmsException("Login succeeded but no auth");// fatal - - // add thread locale to RWT session - // if (log.isTraceEnabled()) - // log.trace("Locale " + LocaleUtils.threadLocale.get()); - // RWT.setLocale(LocaleUtils.threadLocale.get()); - - // once the user is logged in, longer session timeout - RWT.getRequest().getSession() - .setMaxInactiveInterval(sessionTimeout); - - if (log.isDebugEnabled()) - log.debug("Authenticated " + subject); - } catch (FailedLoginException e) { - MessageDialog.openInformation(display.getActiveShell(), - "Bad Credentials", e.getMessage()); - // retry login - continue tryLogin; - } catch (CredentialNotFoundException e) { - MessageDialog.openInformation(display.getActiveShell(), - "No Credentials", e.getMessage()); - // retry login - continue tryLogin; - } catch (LoginException e) { - callbackHandler.getShell().dispose(); - return processLoginDeath(display, e); - } - } - } - final String username = subject.getPrincipals(X500Principal.class) - .iterator().next().getName(); - // Logout callback when the display is disposed - display.disposeExec(new Runnable() { - public void run() { - if (log.isTraceEnabled()) - log.trace("Display disposed"); - try { - LoginContext loginContext = new LoginContext( - AuthConstants.LOGIN_CONTEXT_USER, subject); - loginContext.logout(); - } catch (LoginException e) { - log.error("Error when logging out", e); - } - } - }); - - // - // RUN THE WORKBENCH - // - Integer returnCode = null; - try { - returnCode = Subject.doAs(subject, new PrivilegedAction() { - public Integer run() { - // add security context to session - httpSession.setAttribute(ACCESS_CONTROL_CONTEXT, - AccessController.getContext()); - - // start workbench - RapWorkbenchAdvisor workbenchAdvisor = createRapWorkbenchAdvisor(username); - int result = PlatformUI.createAndRunWorkbench(display, - workbenchAdvisor); - return new Integer(result); - } - }); - // Explicit exit from workbench - fullLogout(subject, username); - } finally { - display.dispose(); - } - return returnCode; - } - - private Integer processLoginDeath(Display display, Throwable e) { - // check thread death - ThreadDeath td = wasCausedByThreadDeath(e); - if (td != null) { - display.dispose(); - throw td; - } - if (!display.isDisposed()) { - ErrorFeedback.show("Unexpected exception during authentication", e); - // this was not just bad credentials or death thread - RWT.getRequest().getSession().setMaxInactiveInterval(1); - display.dispose(); - return -1; - } else { - throw new CmsException( - "Unexpected exception during authentication", e); - } - - } - - /** - * If there is a {@link ThreadDeath} in the root causes, rethrow it - * (important for RAP cleaning mechanism) - */ - protected ThreadDeath wasCausedByThreadDeath(Throwable t) { - if (t instanceof ThreadDeath) - return (ThreadDeath) t; - if (t instanceof ThreadDeathLoginException) - return ((ThreadDeathLoginException) t).getThreadDeath(); - if (t.getCause() != null) - return wasCausedByThreadDeath(t.getCause()); - else - return null; - } - - private void fullLogout(Subject subject, String username) { - try { - LoginContext loginContext = new LoginContext( - AuthConstants.LOGIN_CONTEXT_USER, subject); - loginContext.logout(); - HttpServletRequest httpRequest = RWT.getRequest(); - HttpSession httpSession = httpRequest.getSession(); - httpSession.setAttribute(ACCESS_CONTROL_CONTEXT, null); - RWT.getRequest().getSession().setMaxInactiveInterval(1); - log.info("Logged out " + (username != null ? username : "") - + " (THREAD=" + Thread.currentThread().getId() + ")"); - } catch (LoginException e) { - log.error("Error when logging out", e); - } - } -} diff --git a/org.argeo.security.ui.rap/src/org/argeo/security/ui/rap/SecureRapActivator.java b/org.argeo.security.ui.rap/src/org/argeo/security/ui/rap/SecureRapActivator.java deleted file mode 100644 index b3d7c2337..000000000 --- a/org.argeo.security.ui.rap/src/org/argeo/security/ui/rap/SecureRapActivator.java +++ /dev/null @@ -1,38 +0,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. - */ -package org.argeo.security.ui.rap; - -import org.osgi.framework.BundleActivator; -import org.osgi.framework.BundleContext; - -/** Configure Equinox login context from the bundle context. */ -public class SecureRapActivator implements BundleActivator { - public final static String ID = "org.argeo.security.ui.rap"; - - private static BundleContext bundleContext; - - public void start(BundleContext bc) throws Exception { - bundleContext = bc; - } - - public void stop(BundleContext context) throws Exception { - bundleContext = null; - } - - public static BundleContext getBundleContext() { - return bundleContext; - } -} diff --git a/org.argeo.security.ui.rap/src/org/argeo/security/ui/rap/commands/OpenHome.java b/org.argeo.security.ui.rap/src/org/argeo/security/ui/rap/commands/OpenHome.java deleted file mode 100644 index 37ebe3572..000000000 --- a/org.argeo.security.ui.rap/src/org/argeo/security/ui/rap/commands/OpenHome.java +++ /dev/null @@ -1,46 +0,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. - */ -package org.argeo.security.ui.rap.commands; - -import org.argeo.eclipse.ui.dialogs.ErrorFeedback; -import org.argeo.eclipse.ui.workbench.CommandUtils; -import org.argeo.security.ui.UserHomePerspective; -import org.eclipse.core.commands.AbstractHandler; -import org.eclipse.core.commands.ExecutionEvent; -import org.eclipse.core.commands.ExecutionException; -import org.eclipse.ui.WorkbenchException; -import org.eclipse.ui.handlers.HandlerUtil; - -/** Default action of the user menu */ -public class OpenHome extends AbstractHandler { - private final static String PROP_OPEN_HOME_CMD_ID = "org.argeo.ui.openHomeCommandId"; - - public Object execute(ExecutionEvent event) throws ExecutionException { - - String defaultCmdId = System.getProperty(PROP_OPEN_HOME_CMD_ID, ""); - if (!"".equals(defaultCmdId.trim())) - CommandUtils.callCommand(defaultCmdId); - else { - try { - HandlerUtil.getActiveSite(event).getWorkbenchWindow() - .openPage(UserHomePerspective.ID, null); - } catch (WorkbenchException e) { - ErrorFeedback.show("Cannot open home perspective", e); - } - } - return null; - } -} diff --git a/org.argeo.security.ui.rap/src/org/argeo/security/ui/rap/commands/UserMenu.java b/org.argeo.security.ui.rap/src/org/argeo/security/ui/rap/commands/UserMenu.java deleted file mode 100644 index 98674303b..000000000 --- a/org.argeo.security.ui.rap/src/org/argeo/security/ui/rap/commands/UserMenu.java +++ /dev/null @@ -1,30 +0,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. - */ -package org.argeo.security.ui.rap.commands; - -import org.eclipse.core.commands.AbstractHandler; -import org.eclipse.core.commands.ExecutionEvent; -import org.eclipse.core.commands.ExecutionException; - -/** Default action of the user menu */ -public class UserMenu extends AbstractHandler { - - @Override - public Object execute(ExecutionEvent event) throws ExecutionException { - return null; - } - -} diff --git a/org.argeo.security.ui/pom.xml b/org.argeo.security.ui/pom.xml index 5672e9b93..beb37408e 100644 --- a/org.argeo.security.ui/pom.xml +++ b/org.argeo.security.ui/pom.xml @@ -8,7 +8,7 @@ .. org.argeo.security.ui - Commons CMS Workbench + CMS Workbench jar @@ -16,11 +16,11 @@ org.argeo.cms 2.1.46-SNAPSHOT - - org.argeo.commons - org.argeo.security.core - 2.1.46-SNAPSHOT - + + + + + org.argeo.commons org.argeo.eclipse.ui.workbench diff --git a/org.argeo.server.jcr/.classpath b/org.argeo.server.jcr/.classpath deleted file mode 100644 index a8a298a3c..000000000 --- a/org.argeo.server.jcr/.classpath +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - diff --git a/org.argeo.server.jcr/.project b/org.argeo.server.jcr/.project deleted file mode 100644 index 977218c59..000000000 --- a/org.argeo.server.jcr/.project +++ /dev/null @@ -1,28 +0,0 @@ - - - org.argeo.server.jcr - - - - - - org.eclipse.jdt.core.javabuilder - - - - - org.eclipse.pde.ManifestBuilder - - - - - org.eclipse.pde.SchemaBuilder - - - - - - org.eclipse.pde.PluginNature - org.eclipse.jdt.core.javanature - - diff --git a/org.argeo.server.jcr/bnd.bnd b/org.argeo.server.jcr/bnd.bnd deleted file mode 100644 index 5645d300d..000000000 --- a/org.argeo.server.jcr/bnd.bnd +++ /dev/null @@ -1,12 +0,0 @@ -Import-Package: junit.framework;resolution:=optional,\ -org.xml.sax;version="0.0.0",\ -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,\ -org.h2;resolution:=optional,\ -org.postgresql;resolution:=optional,\ -* -Export-Package: org.argeo.jcr.*, org.argeo.jackrabbit.* \ No newline at end of file diff --git a/org.argeo.server.jcr/build.properties b/org.argeo.server.jcr/build.properties deleted file mode 100644 index f4baf37e8..000000000 --- a/org.argeo.server.jcr/build.properties +++ /dev/null @@ -1,23 +0,0 @@ -source.. = src/,\ - ext/test/ -output.. = bin/ -bin.includes = META-INF/,\ - . -additional.bundles = org.junit,\ - org.apache.jackrabbit.core,\ - javax.jcr,\ - org.apache.jackrabbit.api,\ - org.apache.jackrabbit.data,\ - org.apache.jackrabbit.jcr.commons,\ - org.apache.jackrabbit.spi,\ - org.apache.jackrabbit.spi.commons,\ - org.slf4j.api,\ - org.slf4j.commons.logging,\ - org.slf4j.log4j12,\ - org.apache.log4j,\ - org.apache.commons.collections,\ - EDU.oswego.cs.dl.util.concurrent,\ - org.apache.lucene,\ - org.apache.tika.core,\ - org.apache.commons.dbcp,\ - org.apache.commons.pool diff --git a/org.argeo.server.jcr/ext/test/log4j.properties b/org.argeo.server.jcr/ext/test/log4j.properties deleted file mode 100644 index 3d7528963..000000000 --- a/org.argeo.server.jcr/ext/test/log4j.properties +++ /dev/null @@ -1,14 +0,0 @@ -log4j.rootLogger=WARN, console - -## Levels -log4j.logger.org.argeo=DEBUG -log4j.logger.org.apache.jackrabbit=OFF - -## Appenders -# console is set to be a ConsoleAppender. -log4j.appender.console=org.apache.log4j.ConsoleAppender - -# console uses PatternLayout. -log4j.appender.console.layout=org.apache.log4j.PatternLayout -#log4j.appender.console.layout.ConversionPattern= %-5p %d{ISO8601} %m - %c%n -log4j.appender.console.layout.ConversionPattern=%m%n diff --git a/org.argeo.server.jcr/ext/test/org/argeo/jcr/docbook/DocBookModelTest.java b/org.argeo.server.jcr/ext/test/org/argeo/jcr/docbook/DocBookModelTest.java deleted file mode 100644 index 5af20bad4..000000000 --- a/org.argeo.server.jcr/ext/test/org/argeo/jcr/docbook/DocBookModelTest.java +++ /dev/null @@ -1,115 +0,0 @@ -package org.argeo.jcr.docbook; - -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.InputStream; -import java.io.InputStreamReader; - -import javax.jcr.RepositoryException; - -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.argeo.jackrabbit.unit.AbstractJackrabbitTestCase; -import org.argeo.jcr.JcrUtils; - -public class DocBookModelTest extends AbstractJackrabbitTestCase { - private final static Log log = LogFactory.getLog(DocBookModelTest.class); - - public void testLoadWikipediaSample() throws Exception { - importXml("WikipediaSample.dbk.xml"); - } - - public void XXXtestLoadHowTo() throws Exception { - importXml("howto.xml", false); - } - - protected void importXml(String res) throws Exception { - importXml(res, true); - } - - protected void importXml(String res, Boolean mini) throws Exception { - byte[] bytes; - try (InputStream in = getClass().getResourceAsStream(res)) { - bytes = IOUtils.toByteArray(in); - } - - {// cnd - long begin = System.currentTimeMillis(); - if (mini) { - InputStreamReader reader = new InputStreamReader(getClass() - .getResourceAsStream( - "/org/argeo/jcr/docbook/docbook.cnd")); - CndImporter.registerNodeTypes(reader, session()); - reader.close(); - } else { - InputStreamReader reader = new InputStreamReader(getClass() - .getResourceAsStream( - "/org/argeo/jcr/docbook/docbook-full.cnd")); - CndImporter.registerNodeTypes(reader, session()); - reader.close(); - } - long duration = System.currentTimeMillis() - begin; - if (log.isDebugEnabled()) - log.debug(" CND loaded in " + duration + " ms"); - } - - String testPath = "/" + res; - // if (mini) - JcrUtils.mkdirs(session(), testPath, "dbk:set"); - // else - // JcrUtils.mkdirs(session(), testPath, "dbk:book"); - - DocBookModel model = new DocBookModel(session()); - try (InputStream in = new ByteArrayInputStream(bytes)) { - long begin = System.currentTimeMillis(); - model.importXml(testPath, in); - long duration = System.currentTimeMillis() - begin; - if (log.isDebugEnabled()) - log.debug("Imported " + res + " " + (bytes.length / 1024l) - + " kB in " + duration + " ms (" - + (bytes.length / duration) + " B/ms)"); - } - - saveSession(); - // JcrUtils.debug(session().getRootNode()); - - try (ByteArrayOutputStream out = new ByteArrayOutputStream()) { - try { - model.exportXml(testPath + "/dbk:book", out); - } catch (Exception e) { - model.exportXml(testPath + "/dbk:article", out); - } - bytes = out.toByteArray(); - - session().logout(); - model.setSession(session()); - - // log.debug(new String(bytes)); - try (InputStream in = new ByteArrayInputStream(bytes)) { - long begin = System.currentTimeMillis(); - model.importXml(testPath, in); - long duration = System.currentTimeMillis() - begin; - if (log.isDebugEnabled()) - log.debug("Re-imported " + res + " " - + (bytes.length / 1024l) + " kB in " + duration - + " ms (" + (bytes.length / duration) + " B/ms)"); - } - } - saveSession(); - } - - protected void saveSession() throws RepositoryException { - long begin = System.currentTimeMillis(); - session().save(); - long duration = System.currentTimeMillis() - begin; - if (log.isDebugEnabled()) - log.debug(" Session save took " + duration + " ms"); - } - - // public static Test suite() { - // return defaultTestSuite(DocBookModelTest.class); - // } - -} diff --git a/org.argeo.server.jcr/ext/test/org/argeo/jcr/docbook/WikipediaSample.dbk.xml b/org.argeo.server.jcr/ext/test/org/argeo/jcr/docbook/WikipediaSample.dbk.xml deleted file mode 100644 index 29f5b709b..000000000 --- a/org.argeo.server.jcr/ext/test/org/argeo/jcr/docbook/WikipediaSample.dbk.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - Very simple book - - Chapter 1 - Hello world! - I hope that your day is proceeding splendidly! - - - Chapter 2 - Hello again, world! - - \ No newline at end of file diff --git a/org.argeo.server.jcr/ext/test/org/argeo/jcr/docbook/howto.xml b/org.argeo.server.jcr/ext/test/org/argeo/jcr/docbook/howto.xml deleted file mode 100644 index b8b022aac..000000000 --- a/org.argeo.server.jcr/ext/test/org/argeo/jcr/docbook/howto.xml +++ /dev/null @@ -1,2295 +0,0 @@ - - - -YES"> -NO"> -]> - -
- -DocBook V5.0 -The Transition Guide - - -Jirka Kosek - jirka@kosek.cz -Norman Walsh - ndw@nwalsh.com - §convert4to5, proofreading -Dick Hamilton - rlhamilton@frii.com - §changes-removed, customization, proofreading -Michael(tm) Smith - smith@sideshowbarker.net - §dbxsl-ns - - - -2009-06-16 -2008-02-06 -2007-10-28 -2006-10-22 -2006-05-16 -2006-03-01 -2005-12-28 -2005-10-27 - - - -This document is targeted at DocBook users who are considering -switching from DocBook V4.x to DocBook V5.0. It describes -differences between DocBook V4.x and V5.0 and provides some suggestions about -how to edit and process DocBook V5.0 documents. There is -also a section devoted to conversion of legacy documents from DocBook -4.x to DocBook V5.0. - -At the time this was written the current version of DocBook V5.0 -was &version;. However, almost all of the information in this document is -general and applies to any newer version of DocBook V5.0. - - -
-Introduction - -The differences between DocBook V4.x and V5.0 are quite radical in -some aspects, but the basic idea behind DocBook is still the same, and -almost all element names are unchanged. Because of this it is very -easy to become familiar with DocBook V5.0 if you know any previous version of -DocBook. You can find a complete list of changes in -DB5SPEC, here we will discuss only the most -fundamental changes. - -
-Finally in a namespace - -All DocBook V5.0 elements are in the namespace -http://docbook.org/ns/docbook. XMLExtensible -Markup Language namespaces are used to distinguish -between different element sets. In the last few years, almost all new -XML grammars have used their own namespace. It is easy to -create compound documents that contain elements from different XML -vocabularies. DocBook V5.0 is following this design rule. Using -namespaces in your documents is very easy. Consider this -simple article marked up in DocBook V4.5: - - - Sample article - This is a really short article. -
]]> - -The corresponding DocBook V5.0 article will look very similar: - - - Sample article - This is a really short article. -]]> - -The only change is the addition of a default namespace declaration -(xmlns="http://docbook.org/ns/docbook") on the root -element. This declaration applies the namespace to the root element and -all nested elements. Each -element is now uniquely identified by its local name and namespace. - - -The namespace name http://docbook.org/ns/docbook serves -only as an identifier. This resource is not fetched during processing -of DocBook documents, and you are not required to have an Internet -connection during processing. If you access the namespace URI with a browser, -you will find a short explanatory document about the namespace. In the -future this document will probably conform to (some version of) RDDL -and provide pointers to related resources. - - - - -
-Relaxing with DocBook - -For more than a decade, the DocBook schema was defined using a -DTD. However, DTDs have serious limitations, and DocBook V5.0 is thus -defined using a very powerful schema language called RELAX NG. Thanks -to RELAX NG, it is now much easier to create customized versions of -DocBook, and some content models are now cleaner and more -precise. - -Using RELAX NG has an impact on the document prolog. The following -example shows the typical prolog of a DocBook V4.x document. The version of -the DocBook DTD (in this case 4.5) is indicated in the document type -declaration (!DOCTYPE) which points to a particular version of the -DTD. - - -DocBook V4.5 document - - -
- Sample article - This is a very short article. -
]]>
-
- -In contrast, DocBook V5.0 does not depend on DTDs anymore. This -mean that there is no document type declaration and the version of DocBook -used is indicated with the version -attribute instead. - - -DocBook V5.0 document - -
- Sample article - This is a very short article. -
]]>
-
- -As you can see, DocBook V5.0 is built on top of existing XML -standards as much as possible, for example the lang attribute is superseded by the standard -xml:lang attribute. - -Another fundamental change is that there is no direct indication -of the schema used. Later in this document, you will learn how you can -specify a schema to be used for document validation. - - -Although we recommend the RELAX NG schema for DocBook -V5.0, there are also DTD and W3C XML Schema versions available (see ) for tools that do not yet support RELAX NG. - - -
- -
-Why switch to DocBook V5.0? - -The simple answer is because DocBook V5.0 is the -future. Apart from this marketing blurb, there are also more -technical reasons: - - - -DocBook V4.x is feature frozen.DocBook V4.5 -is the last version of DocBook in the V4.x series. Any new DocBook -development, like the addition of new elements, will be done in -DocBook V5.0. It is only matter of time before useful, new elements -will be added into DocBook V5.0, but they are not likely to be back -ported into DocBook V4.x. DocBook V4.x will be in maintenance mode and -errata will be published if necessary. - - -DocBook V5.0 offers new functionality. -DocBook V5.0 provides significant improvements over DocBook V4.x. For -example there is general markup for annotations, a new and flexible -system for linking, and unified markup for information sections using -the info element. - - -DocBook V5.0 is more extensible. Having -DocBook V5.0 in a separate namespace allows you to easily mix DocBook -markup with other XML-based languages like SVG, MathML, XHTML or even -FooBarML. - - -DocBook V5.0 is easier to customize. RELAX -NG offers many powerful constructs that make customization much easier -than it would be using a DTD (see ). - - - -
- -
-Schema jungle - -Schemas for DocBook V5.0 are available in several formats at - (or the -mirror at ). -Only the RELAX NG schema is normative -and it is preferred over the other schema languages. However, for your -convenience there are also DTD and W3C XML Schema versions provided for DocBook -V5.0. But please note that neither the DTD nor the W3C XML schema are able to -capture all the constraints of DocBook V5.0. This mean that a -document that validates against the DTD or XML schema is not necessarily -valid against the RELAX NG schema and thus may not be a valid -DocBook V5.0 document. See for -summary of constraints that are checked by different schemas. - -DTD and W3C XML Schema versions of the DocBook V5.0 grammar are provided -as a convenience for users who want to use DocBook V5.0 with legacy tools -that don't support RELAX NG. Authors are encouraged to switch to RELAX -NG based tools as soon as possible, or at least to validate documents -against the RELAX NG schema before further processing. - -Some document constraints can't be expressed in schema languages -like RELAX NG or W3C XML Schema. To check for these additional -constraints DocBook V5.0 uses Schematron. We recommend that you -validate your document against both the RELAX NG and -Schematron schemas. - - - Schema Comparison - - - - - - - - - - Description - DTD - W3C XML Schema - W3C XML Schema + Schematron - RELAX NG - RELAX NG + Schematron/NVDL - - - - - Basic document structure - &yes; - &yes; - &yes; - &yes; - &yes; - - - ID/IDREF datatypes - &yes; - &yes; - &yes; - &yes; - &yes; - - - Datatypes - In a very few places RELAX NG specifies datatype - like number (mainly for length specifications) or - enumeration between 0 and - 1. - In general those datatypes can be also supported in - W3C XML Schema, but currently this schema is generated - from DTD which lacks datatype information. - - - &no; - &no; - &no; - &yes; - &yes; - - - Co-occurrences - RELAX NG grammar enforces exclusivity of several - elements. For example if you have title inside - info then it is not allowed to have another - title outside info. Similarly, - models of HTML and CALS tables are separated and validated - properly, where in DTD and WXS only union of both models is - available. - On other places co-occurrences enforces particular - content model based on presence of specific attribute or - attribute value. - Please also note that in theory co-occurences can be - validated using Schematron, but the current DocBook schema - uses RELAX NG for these definitions. Schematron can be used - only for validation, whereas grammar based schemas like - RELAX NG are useful also for other purposes like guided editing. - - &no; - &no; - &no; - &yes; - &yes; - - - Hooks for MathML and SVG content - &no; - &no; - &no; - &yes; - &yes; - - - Link type integrity - Check whether ID/IDREF links are pointing to element - of corresponding type. For example that - footnoteref points to - footnote. - &no; - &no; - &yes; - &no; - &yes; - - - Presence of version - attribute on the root element - &no; - &no; - &yes; - &no; - &yes; - - - Miscellaneous checks - For example consistency of segmented lists, only one - term inside term definition etc. - &no; - &no; - &yes; - &no; - &yes; - - - Element exclusions - Prevents improper nesting of elements, like admonition - inside admonition. - &no; - &no; - &yes; - &no; - &yes; - - - -
- -
-Where to get the schemas - -The latest versions of schemas can be obtained from . At the time this was -written the latest version was &version;. Individual schemas are -available at the following locations: - - - -RELAX NG schema - - - -RELAX NG schema in compact syntax - - - -DTD - - - -W3C XML Schema - - - -Schematron schema with additional checks - - - - -These schemas are also available from the mirror at -. - -
- -
-DocBook documentation - -Detailed documentation about each DocBook V5.0 element is -presented in the reference part -of DocBook: The Definitive Guide. - - -Other parts of DocBook: The Definitive -Guide have not yet been updated to reflect the changes -made in DocBook V5.0. Please do not be confused by this. - - -
- -
- - - -
-Tool chain - -This section briefly describes tools and procedures to edit and -process content stored in DocBook V5.0. - -
-Editing DocBook V5.0 - -Because DocBook is an XML-based format and XML is a text-based -format, you can use any text editor to create and edit DocBook V5.0 -documents. However, using dumb editors like Notepad is -not very productive. You will do better if you use an editor that -supports XML. Although there are DTD and W3C XML Schemas available for -DocBook V5.0, which means you can use any editor that works with DTDs -or W3C XML Schemas, we recommend that you use the RELAX NG grammar -with DocBook V5.0. The rest of this section contains an overview of -XML editors (listed in alphabetical order) that are known to work with -RELAX NG schemas and that offer guided editing based on the RELAX NG -schema. - -
-Emacs and nXML - -nXML -mode is an add-on for the GNU -Emacs text editor. By installing nXML you can turn Emacs -into a very powerful XML editor that offers guided editing and -validation of XML documents. - -
-Emacs with nXML mode provides guided editing and validation - - - - - - - - -
- -nXML uses a special configuration file named -schemas.xml to associate schemas with XML -documents. Often you will find this file in the directory -site-lisp/nxml/schema inside the Emacs installation -directory. Adding the following line into the configuration file, -will associate DocBook V5.0 elements with the appropriate -schema: - -<namespace ns="http://docbook.org/ns/docbook" uri="/path/to/docbook.rnc"/> - - -Please note that nXML ships with a file named -docbook.rnc. This file contains the RELAX NG grammar -for DocBook V4.x. Be sure that you associate the DocBook V5.0 namespace -with the corresponding DocBook V5.0 grammar. - - -If you can't edit the global schemas.xml file, -you can create this file in the same directory as your document. nXML will -find associations placed there also. In this case you must create a -complete configuration file like: - -<locatingRules xmlns="http://thaiopensource.com/ns/locating-rules/1.0"> - <namespace ns="http://docbook.org/ns/docbook" uri="/path/to/docbook.rnc"/> -</locatingRules> - -
- -
-oXygen - -oXygen is a feature -rich XML editor. It has built-in support for many schema languages -including RELAX NG and it is preconfigured with many document types -including DocBook. oXygen will assist you with writing DocBook V5.0 -content, and you will be able to validate your documents against both -RELAX NG and Schematron schemas. - -
-DocBook V5.0 document opened in oXygen - - - - - -
- -
-DocBook V5.0 document opened in oXygen in Author mode - - - - - -
- -
- -
-XML Mind XML editor - -XML -Mind XML editor (XXE) is a visual validating XML editor that -provides a wordprocessor-like interface to users. It is available in -two versions, Standard and Professional. The Standard version is free and -provides everything you need to edit DocBook V5.0 documents. - -
-XML Mind XML Editor – feels almost like MS Word but real DocBook V5.0 markup is created - - - - - -
- -In order to use DocBook V5.0 in XXE you have to install -an add-on. Go to -OptionsInstall -Add-ons…. Then choose DocBook -5 configuration and press the OK -button. After restart, XXE is ready to work with DocBook V5.0 -documents. - -
- -
- -
-Validating DocBook V5.0 - -If you are not using a RELAX NG-based validating editor when you -create documents, we strongly recommend that you validate your -documents against RELAX NG and Schematron schemas before processing -them. Only after successful validation can you be sure that your -document is really DocBook V5.0 and that processing tools will be able -to process it correctly. - -For validation you can use tools that support simultaneous RELAX NG and -Schematron validation, or you can use NVDL to orchestrate validation using -the two schemas. - -
-Using RELAX NG and Schematron - -You can find a list of RELAX NG validators at . It is best to use -validators with support for embedded Schematron rules inside RELAX NG -schemas. Schematron is a rule-based validation language which is used -to impose additional constraints on DocBook documents. Schematron rules -assert conditions which are impossible or difficult to express -in a pure RELAX NG schema. - -Sun -Multi-Schema XML Validator (MSV) is able to validate an XML -document against a RELAX NG schema and Schematron rules at the same time. -To install and use MSV follow these steps: - - - -Download relames.zip from . - - -Unpack the downloaded file into an arbitrary directory. - - -Validate your document using the following command: -java -Xss512K -jar /path/to/relames.jar /path/to/docbook.rng document.xml - -The switch increases the stack size -of the Java virtual machine. This is necessary because the DocBook schema is -quite large. If you get stack overflow errors from MSV, increase -this value. You may get spurious error messages if the value -is too small, so if you get a stack overflow error, ignore any other error -messages and try a larger value for the stack size. -If you are not using Sun's Java implementation, please consult the -documentation for your virtual machine to learn how to increase the stack -size. - - - - -There is also an on-line DocBook V5.0 -validator that validates DocBook V5.0 documents against the normative -RELAX NG schema with embedded Schematron rules. - -
- -
-Using NVDL - -NVDL is a meta-schema language which can validate a document -against several schemas. DocBook V5.0 comes with a NVDL -schema which specifies that DocBook documents should be validated -against both RELAX NG and Schematron schemas. - -You can find a list of NVDL validators at . The following procedures show how to -install and use the oNVDL and -JNVDL -validators. - - -oNVDL installation and usage - -Download onvdl-20070517.zip. - - -Unpack the downloaded file into an arbitrary directory. - - -Validate your document using the following command: -java -jar /path/to/oNVDL/bin/onvdl.jar /path/to/docbook.nvdl document.xml - - - - -JNVDL installation and usage - -Download the latest release of JNVDL from . - - -Unpack the downloaded file into an arbitrary directory. - - -Modify file jnvdl.bat (or jnvdl.sh on Unix based systems) to include switch directly after java command. - - -On Windows systems, validate your document using the following command: -/path/to/jnvdl/jnvdl -nt -s /path/to/docbook.nvdl document.xml -On Unix systems, validate your document using the following command: -/path/to/jnvdl/jnvdl.sh -nt -s /path/to/docbook.nvdl document.xml - - - -
- -
- -
-Processing DocBook V5.0 - -Part of DocBook's great success can be attributed to the -availability of free -tools that can be used to transform DocBook content into various -target formats including HTML and PDF. The DocBook XSL Stylesheets are -very popular tools. - -
-DocBook XSL Stylesheets - -The DocBook stylesheets are designed to process content written in -different versions of DocBook (for example 3.1 and 4.2). Recent -versions of the stylesheets are also able to process DocBook V5.0 -with some limitations. - -You can process DocBook V5.0 documents with the DocBook XSL -stylesheets in exactly the same way you process DocBook V4.x documents. -You do not need special software; you can stick to your preferred -XSLT processor, be it Saxon, xsltproc, Xalan or whatever else (but see -the note about the lost base URI below). - -During document processing, the stylesheets strip -namespaces from DocBook V5.0 to get a document which will be -very similar to DocBook V4.x. This is necessary because from the XSLT -point of view, elements from different namespaces are distinct and cannot -be easily processed by the same set of templates. This process is -completely transparent to the user. If you are processing DocBook V5.0 -documents, the only difference is that you will see the following -additional message: - -Note: namesp. cut : stripped namespace before processing -Note: namesp. cut : processing stripped document - -Although you can successfully use the existing stylesheets to -process DocBook V5.0, there are some limitations and unsupported -features. The unsupported features include: - - -general annotations; -general XLink links on all elements. - - - -During namespace stripping, the base URI of the document is -lost. This means that in rare situations, relatively referenced -resources like images or programlistings can be processed incorrectly. -The stylesheets attempt to compensate for this problem, but that is not always -possible. When an XSLT processor other than Saxon or Xalan is used, a warning -message is generated: - -WARNING: cannot add @xml:base to node set root element. Relative paths may not work. - - - -
- -
-DocBook XSL-NS Stylesheets -As you can see from reading the previous section, namespace - stripping has limitations that will cause trouble in some - situations. To overcome those limitations, Bob Stayton created a - build system for taking the non-namespace-aware DocBook XSL - stylesheets and generating namespace-aware versions from them. - The DocBook XSL-NS stylesheets are the result. - -The DocBook XSL-NS stylesheets are released side-by-side - with the DocBook XSL stylesheets, as a separate docbook-xsl-ns package. They are the -recommended XSLT 1.0 stylesheets to use for transforming -namespaced (DocBook V5.0) documents. -
- -
-XSLT 2.0 based re-implementation - -XSLT 1.0 is missing some important features. To work around -these missing features, the current DocBook XSL stylesheets use some -implementation-specific extensions. -XSLT 2.0 adds many new and previously missing features into the language. -A new set of DocBook stylesheets is being implemented based on XSLT 2.0 -to take advantage of these features and to fully support DocBook V5.0. - - -The XSLT 2.0 based stylesheets have many new features, including: - - -seamless integration of profiling (conditional -documents) with external bibliographies and -glossaries; -no need for (most) external extensions; -internationalized indexes; -easy to customize titlepage templates. - - -The XSLT 2.0 based stylesheets are still under development. At -this writing, they only support HTML and chunked HTML output. As time -permits, the stylesheet developers will be adding other formats. Since -the stylesheets are developed in the limited free time the developers -have, there's no specific schedule. - -There are not very many XSLT 2.0 implementations available. -But, if you want to try the new stylesheets, grab a snapshot of -the development version from -and unpack it somewhere. Then download and install Saxon 9 from . - -To transform a DocBook V5.0 document to a single HTML page use the command: - -java -jar /path/to/saxon9.jar -o output.html document.xml /path/to/docbook-xsl2-snapshot/html/docbook.xsl - -To transform a DocBook V5.0 document to a set of chunked HTML pages use the command: - -java -jar /path/to/saxon9.jar document.xml /path/to/docbook-xsl2-snapshot/html/chunk.xsl - -
- -
- -
- -
-Markup changes - -This section describes the most common markup changes -between DocBook V4.x and V5.0. -You can find a complete list of changes in -DB5SPEC. - -
-Improved cross-referencing and linking - -In DocBook V4.x the attribute id is -used to assign a unique identifier to an element. In DocBook V5.0 this -attribute is renamed xml:id in order -to comply with XMLID. - -Now you can use almost any inline element as the source of a link, -not just xref or link. For example, the following -DocBook 4.x content: - - - DIR command - ... -
- -
- LS command - This command is a synonym for DIR command. -
]]> - -is written in DocBook V5.0 as: - - - DIR command - ... -
- -
- LS command - This command is a synonym for DIR command. -
]]> - -The linkend attribute was added to all -inline elements together with the href -attribute from the XLink namespace. This means that you can use any inline -element as the source of a hypertext link. To use XLinks you have -to declare the XLink namespace (most often on the root element of your -document): - - - Test article - - Emacs - is my favourite text editor.]]> - … - -The ulink element was removed from DocBook V5.0 -in favor of XLink linking. Instead of the DocBook V4.x ulink -element: - -DocBook site]]> - -you can now use link - -DocBook site]]> - -XLink links may contain a fragment identifier, which you can -use instead of linkend to form -cross-references inside a document; for example: - -DIR]]> - -However XLink links are not checked during validation, while xml:id/linkend -links are checked for ID/IDREF consistency. -One place where the XLink-based, fragment identifier scheme is -useful is when XInclude is being used, since XML ID/IDREF links -cannot span XInclude boundaries. -You can use whichever approach better suits your needs. - - -
-Renamed elements - -Some elements were renamed to better express their meaning or to -reduce the total number of elements available in DocBook. - - -Renamed elements - - - -Old name -New name - - - - -sgmltag -tag - - -bookinfo, articleinfo, -chapterinfo, *info -info - - -authorblurb -personblurb - - -collabname, corpauthor, -corpcredit, corpname -orgname - - -isbn, issn, -pubsnumber -biblioid - - -lot, lotentry, tocback, -tocchap, tocfront, toclevel1, -toclevel2, toclevel3, toclevel4, -toclevel5, tocpart -tocdiv - - -graphic, graphicco, -inlinegraphic, mediaobjectco -mediaobject and inlinemediaobject - - -ulink -link - - -ackno -acknowledgements - - - -
- -
- -
-Removed elements - -The following elements were removed from DocBook V5.0 without -direct replacements: action, beginpage, highlights, -interface, invpartnumber, medialabel, modespec, -structfield, structname. -If you use one or more of these elements, here are some suggestions -as to how to re-code them in DocBook V5.0. - - - -Recommended mapping for removed elements - - - -Old name -Recommended mapping - - - - -action -Use <phrase remap="action">. - - -beginpage -Remove: beginpage is advisory only -and has tended to cause confusion. A processing instruction or -comment should be a workable replacement if one is needed. - - -highlights -Use abstract. Note that because highlights has a broader content model, you may -need to wrap contents in a para inside -abstract. - - -interface -Use one of the gui* elements -(guibutton, guiicon, guilabel, -guimenu, guimenuitem, or -guisubmenu). - - -invpartnumber -Use <biblioid class="other" -otherclass="medialabel">. The -productnumber element is another alternative. - - -medialabel -Use <citetitle -pubwork="mediatype">, -where mediatype is the type of media being -labeled (e.g.,cdrom or dvd). - - -modespec -No longer needed. The current processing model for -olink renders modespec -unnecessary. - - -structfield, structname -Use varname. If you need to distinguish between the -two, use <varname -remap="structname or -structfield">. In some contexts, it -may also be appropriate to use property for structfield. - - - -
- -
- - - -
-Converting DocBook V4.x documents to DocBook V5.0 - -The DocBook V5.0 schema ships with an XSLT 1.0 stylesheet that -is designed to transform valid DocBook V4.x documents to valid -DocBook V5.0 documents. - -To convert your document, doc.xml in the -examples below, follow these steps: - - - -Check the validity of your DocBook XML V4.x document. The -conversion tool assumes that the input document is valid. If the input -document contains markup errors, the results will be unpredictable at -best. - - -Transform doc.xml to -newdoc.xml with the -db4-upgrade.xsl stylesheet included in the -DocBook V5.0 distribution that you are using. - - -Check the validity of your DocBook XML V5.0 document against -the DocBook V5.0 RELAX NG grammar. - - - -In the vast majority of cases, the resulting document should -be valid and your conversion process is finished. - -If the document is not valid, please report the problem. -(Over time, we'll have more experience with the sorts of things -that can go wrong and we'll update this document to reflect that -experience.) - -
-What About Entities? - -Using XSLT to transform existing documents to DocBook V5.0 has -one potential disadvantage: it removes all entity references from -your document. - -If preserving entities is an important aspect of your production -work flow, you will have to engage in a semi-manual process to -preserve them. - - - -Open your existing document using your favorite editing tool. -You must use a tool that is not XML-aware, or one -that allows you to edit markup “in the raw”. - - -Replace all occurrences of the entity references that you want -to preserve with some unique string. For example, if you want to preserve -“&Product;” references, you could replace them -all with “[[[Product]]]” (assuming that the string -“[[[Product]]]” doesn't occur anywhere else in your document). - - -Copy the document type declaration off of your document and save -it some place. The document type declaration is everything from -“<!DOCTYPE” to the closing “]>”. - - - -Perform the conversion described in . - - - -Open the new document using your favorite editing tool. Replace -all occurrences of the unique string you used to save the entity references -with the corresponding entity references. - - -Paste the document type declaration that you saved onto the top -of your new document. - - -Remove the external identifier (the PUBLIC -and/or SYSTEM keywords) from the document type -declaration. A document that begins: - -]>]]> -is perfectly well-formed. If you don't remove the references to -the DTD, then your parser will likely try to validate against DocBook -V4.0 and that's not going to work. Alternatively, you could refer -to the DocBook V5.0 DTD. - - - - -Steps 2 and 5 from previous procedure can be automated using the -cloak -script written by Michael Smith. - - -
-External Parsed Entities - -External parsed entities, entities which load part of a document -from another file, are a special case. These can often be replaced -with XInclude elements. - -The Perl script db4-entities.pl, also included -in the DocBook V5.0 distribution attempts to perform this replacement -for you. To use the script, perform the following steps: - - - -Process your document with db4-entities.pl. -The script expects a single filename and prints the XInclude version -on standard output. - - -Process the XInclude version as described in . - - - -
-
- -
- -
- Customizing DocBook V5.0 - - - - It's much easier to customize DocBook V5.0 than it was to - customize earlier releases. This is partly because RELAX NG - provides better support for modifications than DTDs and partly - because the DocBook schema is designed to take full advantage - of the capabilities RELAX NG provides. - This section describes the organization of the RELAX NG schema for - DocBook, methods and examples for adding, removing, and modifying elements - and attributes, and conventions for naming and versioning - DocBook customizations. - It assumes some familiarity with RELAX NG. If you are unfamiliar - with RELAX NG, you can find a tutorial introduction in - RNCTUT. - -
- DocBook RELAX NG schema organization - - The DocBook RELAX NG schema is highly modular, using named - patterns extensively. Every element, attribute, attribute - list, and enumeration has its own named pattern. In addition, - there are named patterns for logical combinations of elements - and attributes. These named patterns provide hooks - into the schema that allow you to do a wide range of customization - by simply redefining one or more of the named patterns. - - - An important design characteristic of the schema is that - duplication is minimized. This is done through the use of - named patterns for common groupings that can be re-used. - For example, the imagedata and videodata - elements each have an align attribute - that takes the same set of enumerated values. Rather than - repeating those values, a single pattern, - db.halign.enumeration is referenced by - the db.videodata.align.enumeration - and db.imagedata.align.enumeration patterns, - which are in turn referenced by the - db.videodata.align.attribute - and db.imagedata.align.attribute patterns. - While this may seem like overkill, it allows a customizer to modify - the allowed enumerations for these two attributes separately or together, - or to completely re-define the allowed content of either or both, - by redefining one or more of these named patterns. - -
Pattern Names - - Because named patterns are used extensively, the RELAX NG schema uses - several naming conventions. These are: - - - - Names have two or more parts, separated by dots . - - - - - The first part of each name is the prefix db - - - - - Each element has a named pattern in the form - db.elementname. - Elements that have different content models in different - contexts will also have patterns in the form - db.context.elementname. For example, db.figure.info - defines the content model for the info element - when it appears as a child of the figure element. - Context may have several parts. - For example, db.cals.entrytbl.thead. - - - - - Most attributes have a named pattern in the form - db.attributename.attribute. - Attributes that have different content models in different - contexts will also have patterns in the form - db.context.attributename.attribute. - For example, - db.olink.localinfo.attribute defines the content - model of the localinfo attribute when - it appears in olink. - There are a few attributes that do not have individual named - patterns. For example, the effectivity attributes are grouped - into db.effectivity.attributes and not identified - separately. - - - - - Each element has a named pattern for its attribute list in - the form - db.elementname.attlist - - that defines the list of attributes for that element. - Elements that have different attribute lists in different - contexts will also have patterns in the form - db.context.elementname.attlist - For example, db.html.table.attlist defines - the attribute list for the html table element and - db.cals.table.attlist defines the attribute - list for a cals table element. - - - - - Each attribute that has enumerated values has a - named pattern in the form - db.[context.]attributename.enumeration. - If the enumeration for a particular attribute depends on - context, optional context is provided. - For example, - db.verbatim.continuation.enumeration defines - the enumeration values for the - continuation attribute that is used - in verbatim contexts like screen. - Unlike elements and attributes, there is not necessarily a - named pattern for enumerated attributes outside their context. - For example, there is no db.class.enumeration - because the class attribute has - a broad and non-intersecting range of uses. - - - - - There are several different groupings of elements and attributes. - Here are the major ones: - - - inlines - - - Combinations of inline elements, for example, - db.error.inlines, which contains - db.errorcode, - db.errortext, etc. - - - - - blocks - - - Combinations of block elements, for example, - db.verbatim.blocks, which contains - db.programlisting, - db.screen, etc. - - - - - attributes - - - Combinations of attributes, for example, - db.effectivity.attributes, - which contains the attributes - arch, - condition, - conformance, etc. - - - - - components - - - High level components of the schema, for example, - db.navigation.components, which contains - db.glossary, - db.bibliography, - db.index, and - db.toc, and is used inside the - content model for chapter, appendix, - and preface. - - - - - contentmodel - - - Shared content models, for example, - db.admonition.contentmodel, which contains - the content model for tip, warning, - note, etc. - - - - - - - There are a couple of other groupings designed to minimize - duplication, but these are the most important. - - - - -
-
-
- General customization considerations - - Creating a customized schema is similar to - creating a customization layer for XSL. The schema customization - layer is a new RELAX NG schema that defines your changes and - includes the standard docbook schema. You then validate using - the schema customization as your schema. - - - is an empty - RELAX NG customization that does nothing - except define the name spaces and include the standard DocBook schema. - The href attribute of the - include element points to - the location of the standard DocBook V5.0 - schema.The examples in this section use - docbook.rng as the schema location. If you want - to create a portable schema customization you should use a standard - web-accessible location like - http://docbook.org/xml/&version;/rng/docbook.rng and - then use XML - catalogs to resolve this location to your local copy of the - schema for improved performance. Unfortunately, at the time of - this writing not all RELAX NG validators support XML catalogs. - All of the examples are given in both RNG and RNC form. -Empty customization file - - - - - - -]]> - - - -
-
- Elements -
- Adding elements - - Adding an element typically takes two definitions. - The first defines the new element and - its content model, and the second adds the - new element into the schema. We'll show two examples. - - - - adds a new element, - person, with the same - content model as author. The new element will be - allowed to appear wherever author can appear. - - - The db.author pattern is copied - and renamed dbx.person, defining - a new element called person. - Then, the db.author pattern is redefined - to be a choice of the current value or dbx.person. - The combine attribute tells - RELAX NG to combine this pattern with the existing named - pattern. In this case, the value - of the combine attribute is - choice, which tells the parser that either - the original pattern or this new pattern is a valid match. - -Adding a new element by duplicating an existing one - - - - - - - - - - - - - - -]]> - - - - The preceding method works well when you'd like a new element - to be a clone or near-clone of an existing element. It gives - you complete control over the content model, but - only limited control over where the element is allowed. It - works well when you want to allow the element in the same places - as an existing element, and for this example that works - nicely, since author is allowed in four different - named patterns, each of which would have had to be redefined to - allow person. - But, if you can't find an existing element that is allowed in - exactly the places you need, this method doesn't work as well. - - - - adds two new elements by combining them into - a higher level pattern. In this example, we'll add - two new inline elements for writing about assembly language, - register and - instruction. - We will allow them wherever programming inlines - or operating system inlines are allowed. - - defines the two elements, creates a new named pattern - (dbx.asm.inlines) that contains them, and adds - that pattern to db.programming.inlines and - db.os.inlines. Since these two patterns - don't have any elements in common, the strategy used in - - would require selecting two different elements to clone, - which would be messy. - -Adding new inline elements - - - - - - - - - - - - - - - - - - - - - - - - - - - - -]]> - - -
-
- Deleting elements - - Deleting elements is straightforward, but takes some - care and planning. deletes - the important admonition element by redefining - it with a content model of notAllowed. - Note that in this example, the redefinition is inside - the include element. - This is required for - redefinitions that completely replace an existing pattern. - - - Be careful; If you delete an element that is a required part - of another element's content model, you can make it - impossible to create a valid document. - For example, if you delete the title - element, you won't be able to validate a book - because a book requires a title. - -Deleting an element - - - - - - - - -]]> - - -
-
- Customizing the content model of existing elements - - - expands the definition of author to include two - new elements, born and - died. - The author element allows two content models, - db.person.author.contentmodel, which - defines an author who is a person, and - db.org.author.contentmodel, which - defines an author that is an organization. We will modify - db.person.author.contentmodel so that - only authors who are persons can have the new elements. -Modifying the content model of an element - - - - - - - - - - - - - - - - - - -]]> - - - - - This modification will allow instances like this: - - Babe Ruth - 02/06/1895 - 08/16/1948 -]]> -but because we only modified the content model for authors -who are human, it won't allow an instance like this, which -uses db.org.author.contentmodel: - - - Boston Red Sox - 1919 - 2004 -]]> - -
-
-
- Attributes -
- Adding attributes - - The simplest way to add an attribute to a single element - is to add it to the attlist pattern for that element. - - adds the optional attributes born - and died to the attribute - list for author. - The db.author.attlist - named pattern is redefined with the - combine attribute set to - interleave, which interleaves the two new - optional attributes with the existing attributes on the list. - -Adding attributes - - - - - - - - - - - - - - - - - - -]]> - - - - Unlike - , - allows - the new attributes to appear on any author - element, not just those using the person content model. - - - shows - how you could limit the use of these attributes to authors who - are persons. In this example, the new attributes are interleaved - with the db.person.author.contentmodel. - The only difference between this example and - is - that the added patterns are identified as attributes rather than - elements. This shows some of the flexibility of RELAX NG, which - treats attributes and elements very consistently. -Adding attributes; alternate method - - - - - - - - - - - - - - - - - - -]]> - - -There is one difference in the treatment of attributes and elements -that is worth noting. By the XML 1.0 definition, the relative order -of attributes is not significant. Therefore, the -interleave block is not required for -attributes, though it does no harm. - -
-
- Deleting attributes - - Deleting an attribute is similar to deleting an element, - except that you use the RELAX NG empty - pattern rather than notAllowed. - - deletes the linking attributes, which are collected in the - db.common.linking.attributes pattern, - by defining that pattern as empty. - -Deleting an attribute - - - - - - - -]]> - - - - Generally, empty is used when deleting - attributes and notAllowed is used when - deleting elements. - -
-
- Changing permitted content of attributes - - - modifies db.spacing.enumeration to - add the additional value large. Note - that to remove a value from an enumeration, you need - to redefine the entire enumeration, minus the values - you don't need. - -Deleting an attribute - - - - - - large - -]]> - - -
-
- -
-Naming and versioning DocBook customizations - -DocBook V5.0 is not tightly coupled with some particular -validation technology like DTDs. This also means that DocBook V5.0 -documents don't have to (and usually don't) start with a -document type declaration (<!DOCTYPE…>) to specify the schema -(DTD) to use. Instead, DocBook V5.0 instances can be easily -distinguished from other XML vocabularies by using elements in the -http://docbook.org/ns/docbook namespace. This namespace is -enough to distinguish DocBook from other XML based formats. But the -DocBook schema evolves over time and there are several versions of -DocBook (e.g. 3.1, 4.2, 4.5 and 5.0). Since DocBook version 5.0, the -actual version used is indicated in the version attribute on a root element. - - - … -]]> - -Future versions of DocBook documents will start with the same -markup, except the version number will be raised, for example to 5.1 -or 6.0. -The namespace will remain the same until the semantics of the elements -change in a backward incompatible way, which is very unlikely to happen. - -If you create a DocBook schema customization you must change the version attribute to distinguish your -customization from the official DocBook. Changing the -namespace is not recommended because that would break the processing -tools. Remember that changing namespaces is the same as renaming all -elements in the namespace. - -When you customize the schema, use the following syntax to -identify your DocBook derivation: - -base_version-[subset|extension|variant] [name[-version]?]+ - -For example: - -5.0-subset simplified-1.0 -5.0-variant ASMBook -5.0-variant ASMBook-2006 -5.0-extension MathML-2.0 SVG-1.1 - -The first part of the version identifier is the version number of the -DocBook schema from which you derived your customization. - -If your schema is a proper subset, you can advertise this status -by using the subset keyword in the description. If -your schema contains any markup model extensions, you can advertise -this status by using the extension keyword. If -you'd rather not characterize your variant specifically as a subset or -an extension, use the variant keyword. - -After these keywords you may add a whitespace separated list of -customization identifiers. Each name may be optionally followed by its -version number. - -
- -
- -
-FAQ - - - -Authoring - - - -How do I attach a schema to a DocBook V5.0 document when I do not -want to use DTDs and !DOCTYPE? - - -There is no standard way of associating a RELAX NG schema with a -document. Most tools provide some mechanism for performing this -association, consult the documentation for your application. In some -tools you must specify schema manually each time you want to -edit/process your document. - - - - - -How do I use entities like ndash in -DocBook V5.0? - - -Modern schema languages (including RELAX NG and W3X XML Schema) -do not provide any means to define entities that can be used for easier -typing of special characters. Some editors provide functions or -special toolbars that allow you to easily pick necessary character -and insert it into document as a raw Unicode character or a numeric -character reference. -Another possibility is to include entity definitions in the -prolog of your document. Entity definition -files are now maintained by W3C. You can reference definition -files with entity definitions you are interested in and then reference -imported entities. For example: - - -%isopub; -]> -
-DocBook V5.0 – the superb documentation format]]> -… -For your convenience there is also flattened entity definition -file which contains all entity definitions. - - -%allent; -]> -
-DocBook V5.0 – the superb documentation format]]> -… - - - - - -How to modularize documents? - - -You can use XInclude for this -task. There is an alternative schema for DocBook V5.0 that -contains XInclude elements. This is necessary to make some XML editors -happy. This schema can be found in files that end with letters xi, e.g. -docbookxi.rnc instead of -docbook.rnc. - - - - - -How to validate documents which are composed by XInclude? - - -If you are using XIncludes you should make sure that the final -document after resolving all inclusions is valid DocBook V5.0 -instance. This means that all XIncludes should be processed before -validation takes place. The following command can be used to enable -XInclude processing in oNVDL. -java -Dorg.apache.xerces.xni.parser.XMLParserConfiguration=org.apache.xerces.parsers.XIncludeParserConfiguration -jar /path/to/oNVDL/bin/onvdl.jar /path/to/docbook.nvdl document.xml -For JNVDL you can use switch to enable XInclude processing. - - - - - - -Stylesheets - - - -Will the current DocBook XSL stylesheets (XSLT 1.0 based -implementation) be maintained and improved in the future since work on -a new XSLT 2.0 based implementation has started? - - -Yes, the current stylesheets (like 1.73.x) will be supported and -improved further because they are very widely deployed and work with -many existing XSLT processors. -Surely there will be a point in a future when all new development -will be switched to the XSLT 2.0 based implementation. But this -will not happen until all features of the current stylesheets are -implemented in the new stylesheets, and until there is more than -one usable XSLT 2.0 processor available. - - - - - - -Schema customizations - - - -How can I extend the DocBook schema with MathML elements? - - -The basic DocBook schema allows elements from the MathML namespace -to appear inside the equation element. This means that you can -validate a DocBook+MathML document, but MathML content will be ignored -during the validation. You will also not be able to use guided editing -for the MathML content. -If you need strict validation of MathML content or guided -editing for MathML, you can easily extend the base DocBook schema with -the MathML schema. - -Extending the DocBook schema with the MathML schema - -Download the MathML RELAX NG schema from and unpack it -somewhere (e.g. into a mathml subdirectory). - - -Create a schema customization in compact syntax—dbmathml.rnc: -namespace html = "http://www.w3.org/1999/xhtml" -namespace mml = "http://www.w3.org/1998/Math/MathML" -namespace db = "http://docbook.org/ns/docbook" - -include "/path/to/docbook.rnc" { - db._any.mml = external "mathml/mathml2.rnc" - db._any = - element * - (db:* | html:* | mml:*) { - (attribute * { text } - | text - | db._any)* - } -} -Or, alternatively, you can use the XML syntax of RELAX NG—dbmathml.rng: - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -]]> - - -Now use the customized schema (dbmathml.rnc -or dbmathml.rng) instead of the original -DocBook schema. - - - - - - - -How can I extend the DocBook schema with SVG elements? - - -The situation is the same as with MathML support. You can use -elements from the SVG namespace inside the imageobject -element. - -Extending the DocBook schema with the SVG schema - -Download the SVG RELAX NG schema from and unpack it -somewhere (e.g. into an svg subdirectory). - - -Create a schema customization in compact syntax—dbsvg.rnc: -namespace html = "http://www.w3.org/1999/xhtml" -namespace db = "http://docbook.org/ns/docbook" -namespace svg = "http://www.w3.org/2000/svg" - -include "/path/to/docbook.rnc" { - db._any.svg = external "svg/svg11.rnc" - db._any = - element * - (db:* | html:* | svg:*) { - (attribute * { text } - | text - | db._any)* - } -} -Or, alternatively, you can use the XML syntax of RELAX NG—dbsvg.rng: - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -]]> - - -Now use the customized schema (dbsvg.rnc -or dbsvg.rng) instead of the original -DocBook schema. - - - - - - - -Is it possible to use the previous two customizations for MathML -and SVG together? - - -Yes, you can create a special schema customization that combines -both MathML and SVG with the DocBook schema. In compact syntax, the merged -schema is: -namespace html = "http://www.w3.org/1999/xhtml" -namespace mml = "http://www.w3.org/1998/Math/MathML" -namespace db = "http://docbook.org/ns/docbook" -namespace svg = "http://www.w3.org/2000/svg" - -include "/path/to/docbook.rnc" { - db._any.mml = external "mahtml/mathml2.rnc" - db._any.svg = external "svg/svg11.rnc" - db._any = - element * - (db:* | html:* | mml:* | svg:*) { - (attribute * { text } - | text - | db._any)* - } -} -Or alternatively in the full RELAX NG syntax: - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -]]> - - - - - -Are there any other examples of schema customization -available? - - -Sure. Some of the are listed bellow: - -Sample -customization of ITS and DocBook -Examples on -DocBook WiKi - - - - - - - -Tool specific problems - - - -I'm using Altova XMLSpy to validate DocBook V5.0 instances -against the W3C XML Schema (docbook.xsd). XMLSpy -complains about undefined xml:id -attributes? - - -XMLSpy always uses its own bundled version of -xml.xsd which unfortunately doesn't define the xml:id attribute. The bundled version of -xml.xsd is hardwired into the program and cannot -be replaced by a newer version. To solve this problem you must upgrade -to version 2006 SP1. - - - - - - -
- - - - -RNCTUT -Clark, James – Cowan, John – MURATA, Makoto: RELAX NG Compact Syntax Tutorial. -Working Draft, 26 March 2003. OASIS. - - - -NVDLTUT -Nálevka, Petr: -NVDL Tutorial. - - - - -XMLID -Marsh, Jonathan – -Veillard, Daniel – -Walsh, Norman: xml:id Version 1.0. W3C Recommendation, 9 September 2005. - - - -DB5SPEC -Norman, Walsh: The DocBook Schema. -Working Draft 5.0a1, OASIS, 29 June 2005. - - - - - -
diff --git a/org.argeo.server.jcr/ext/test/org/argeo/jcr/tabular/JcrTabularTest.java b/org.argeo.server.jcr/ext/test/org/argeo/jcr/tabular/JcrTabularTest.java deleted file mode 100644 index 8896b2f4b..000000000 --- a/org.argeo.server.jcr/ext/test/org/argeo/jcr/tabular/JcrTabularTest.java +++ /dev/null @@ -1,85 +0,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. - */ -package org.argeo.jcr.tabular; - -import java.io.InputStreamReader; -import java.util.ArrayList; -import java.util.List; - -import javax.jcr.Node; -import javax.jcr.PropertyType; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.apache.jackrabbit.commons.cnd.CndImporter; -import org.argeo.jackrabbit.unit.AbstractJackrabbitTestCase; -import org.argeo.jcr.ArgeoNames; -import org.argeo.jcr.ArgeoTypes; -import org.argeo.util.tabular.TabularColumn; -import org.argeo.util.tabular.TabularRow; -import org.argeo.util.tabular.TabularRowIterator; -import org.argeo.util.tabular.TabularWriter; - -public class JcrTabularTest extends AbstractJackrabbitTestCase { - private final static Log log = LogFactory.getLog(JcrTabularTest.class); - - public void testWriteReadCsv() throws Exception { - session().setNamespacePrefix("argeo", ArgeoNames.ARGEO_NAMESPACE); - InputStreamReader reader = new InputStreamReader(getClass() - .getResourceAsStream("/org/argeo/jcr/argeo.cnd")); - CndImporter.registerNodeTypes(reader, session()); - reader.close(); - - // write - Integer columnCount = 15; - Long rowCount = 1000l; - String stringValue = "test, \ntest"; - - List header = new ArrayList(); - for (int i = 0; i < columnCount; i++) { - header.add(new TabularColumn("col" + i, PropertyType.STRING)); - } - Node tableNode = session().getRootNode().addNode("table", - ArgeoTypes.ARGEO_TABLE); - TabularWriter writer = new JcrTabularWriter(tableNode, header, - ArgeoTypes.ARGEO_CSV); - for (int i = 0; i < rowCount; i++) { - List objs = new ArrayList(); - for (int j = 0; j < columnCount; j++) { - objs.add(stringValue); - } - writer.appendRow(objs.toArray()); - } - writer.close(); - session().save(); - - if (log.isDebugEnabled()) - log.debug("Wrote tabular content " + rowCount + " rows, " - + columnCount + " columns"); - // read - TabularRowIterator rowIt = new JcrTabularRowIterator(tableNode); - Long count = 0l; - while (rowIt.hasNext()) { - TabularRow tr = rowIt.next(); - assertEquals(header.size(), tr.size()); - count++; - } - assertEquals(rowCount, count); - if (log.isDebugEnabled()) - log.debug("Read tabular content " + rowCount + " rows, " - + columnCount + " columns"); - } -} diff --git a/org.argeo.server.jcr/ext/test/org/argeo/server/jcr/JcrResourceAdapterTest.java b/org.argeo.server.jcr/ext/test/org/argeo/server/jcr/JcrResourceAdapterTest.java deleted file mode 100644 index 3ce499af8..000000000 --- a/org.argeo.server.jcr/ext/test/org/argeo/server/jcr/JcrResourceAdapterTest.java +++ /dev/null @@ -1,101 +0,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. - */ -package org.argeo.server.jcr; - -import java.io.InputStream; -import java.text.SimpleDateFormat; -import java.util.Calendar; -import java.util.List; - -import org.apache.commons.io.IOUtils; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.argeo.jackrabbit.unit.AbstractJackrabbitTestCase; -import org.argeo.jcr.JcrResourceAdapter; -import org.springframework.core.io.ClassPathResource; -import org.springframework.core.io.Resource; - -public class JcrResourceAdapterTest extends AbstractJackrabbitTestCase { - private static SimpleDateFormat sdf = new SimpleDateFormat( - "yyyyMMdd:hhmmss.SSS"); - - private final static Log log = LogFactory - .getLog(JcrResourceAdapterTest.class); - - private JcrResourceAdapter jra; - - public void testCreate() throws Exception { - String basePath = "/test/subdir"; - jra.mkdirs(basePath); - Resource res = new ClassPathResource("org/argeo/server/jcr/dummy00.xls"); - String filePath = basePath + "/dummy.xml"; - jra.create(filePath, res.getInputStream(), "application/vnd.ms-excel"); - InputStream in = jra.retrieve(filePath); - assertTrue(IOUtils.contentEquals(res.getInputStream(), in)); - } - - public void testVersioning() throws Exception { - String basePath = "/test/versions"; - jra.mkdirs(basePath); - String filePath = basePath + "/dummy.xml"; - Resource res00 = new ClassPathResource( - "org/argeo/server/jcr/dummy00.xls"); - jra.create(filePath, res00.getInputStream(), "application/vnd.ms-excel"); - Resource res01 = new ClassPathResource( - "org/argeo/server/jcr/dummy01.xls"); - jra.update(filePath, res01.getInputStream()); - Resource res02 = new ClassPathResource( - "org/argeo/server/jcr/dummy02.xls"); - jra.update(filePath, res02.getInputStream()); - - List versions = jra.listVersions(filePath); - log.debug("Versions of " + filePath); - int count = 0; - for (Calendar version : versions) { - log.debug(" " + (count == 0 ? "base" : count - 1) + "\t" - + sdf.format(version.getTime())); - count++; - } - - assertEquals(4, versions.size()); - - InputStream in = jra.retrieve(filePath, 1); - assertTrue(IOUtils.contentEquals(res01.getInputStream(), in)); - in = jra.retrieve(filePath, 0); - assertTrue(IOUtils.contentEquals(res00.getInputStream(), in)); - in = jra.retrieve(filePath, 2); - assertTrue(IOUtils.contentEquals(res02.getInputStream(), in)); - Resource res03 = new ClassPathResource( - "org/argeo/server/jcr/dummy03.xls"); - jra.update(filePath, res03.getInputStream()); - in = jra.retrieve(filePath, 1); - assertTrue(IOUtils.contentEquals(res01.getInputStream(), in)); - } - - @Override - protected void setUp() throws Exception { - log.debug("SET UP"); - super.setUp(); - jra = new JcrResourceAdapter(); - jra.setSession(session()); - } - - @Override - protected void tearDown() throws Exception { - log.debug("TEAR DOWN"); - super.tearDown(); - } -} diff --git a/org.argeo.server.jcr/ext/test/org/argeo/server/jcr/dummy00.xls b/org.argeo.server.jcr/ext/test/org/argeo/server/jcr/dummy00.xls deleted file mode 100644 index e5846fef7..000000000 Binary files a/org.argeo.server.jcr/ext/test/org/argeo/server/jcr/dummy00.xls and /dev/null differ diff --git a/org.argeo.server.jcr/ext/test/org/argeo/server/jcr/dummy01.xls b/org.argeo.server.jcr/ext/test/org/argeo/server/jcr/dummy01.xls deleted file mode 100644 index b5c6b5539..000000000 Binary files a/org.argeo.server.jcr/ext/test/org/argeo/server/jcr/dummy01.xls and /dev/null differ diff --git a/org.argeo.server.jcr/ext/test/org/argeo/server/jcr/dummy02.xls b/org.argeo.server.jcr/ext/test/org/argeo/server/jcr/dummy02.xls deleted file mode 100644 index d73bc6605..000000000 Binary files a/org.argeo.server.jcr/ext/test/org/argeo/server/jcr/dummy02.xls and /dev/null differ diff --git a/org.argeo.server.jcr/ext/test/org/argeo/server/jcr/dummy03.xls b/org.argeo.server.jcr/ext/test/org/argeo/server/jcr/dummy03.xls deleted file mode 100644 index 0759cb927..000000000 Binary files a/org.argeo.server.jcr/ext/test/org/argeo/server/jcr/dummy03.xls and /dev/null differ diff --git a/org.argeo.server.jcr/pom.xml b/org.argeo.server.jcr/pom.xml deleted file mode 100644 index af01e0097..000000000 --- a/org.argeo.server.jcr/pom.xml +++ /dev/null @@ -1,18 +0,0 @@ - - 4.0.0 - - org.argeo.commons - argeo-commons - 2.1.46-SNAPSHOT - .. - - org.argeo.server.jcr - Commons JCR - - - org.argeo.commons - org.argeo.util - 2.1.46-SNAPSHOT - - - \ No newline at end of file diff --git a/org.argeo.server.jcr/src/org/argeo/jackrabbit/JackrabbitAdminLoginModule.java b/org.argeo.server.jcr/src/org/argeo/jackrabbit/JackrabbitAdminLoginModule.java deleted file mode 100644 index 7e17dbca1..000000000 --- a/org.argeo.server.jcr/src/org/argeo/jackrabbit/JackrabbitAdminLoginModule.java +++ /dev/null @@ -1,47 +0,0 @@ -package org.argeo.jackrabbit; - -import java.util.Map; - -import javax.security.auth.Subject; -import javax.security.auth.callback.CallbackHandler; -import javax.security.auth.login.LoginException; -import javax.security.auth.spi.LoginModule; - -import org.apache.jackrabbit.core.security.SecurityConstants; -import org.apache.jackrabbit.core.security.principal.AdminPrincipal; - -public class JackrabbitAdminLoginModule implements LoginModule { - private Subject subject; - - @Override - public void initialize(Subject subject, CallbackHandler callbackHandler, - Map sharedState, Map options) { - this.subject = subject; - } - - @Override - public boolean login() throws LoginException { - // TODO check permission? - return true; - } - - @Override - public boolean commit() throws LoginException { - subject.getPrincipals().add( - new AdminPrincipal(SecurityConstants.ADMIN_ID)); - return true; - } - - @Override - public boolean abort() throws LoginException { - return true; - } - - @Override - public boolean logout() throws LoginException { - subject.getPrincipals().removeAll( - subject.getPrincipals(AdminPrincipal.class)); - return true; - } - -} diff --git a/org.argeo.server.jcr/src/org/argeo/jackrabbit/JackrabbitContainer.java b/org.argeo.server.jcr/src/org/argeo/jackrabbit/JackrabbitContainer.java deleted file mode 100644 index c5ca7952c..000000000 --- a/org.argeo.server.jcr/src/org/argeo/jackrabbit/JackrabbitContainer.java +++ /dev/null @@ -1,351 +0,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. - */ -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.jcr.ArgeoJcrException; -import org.argeo.jcr.ArgeoNames; -import org.argeo.jcr.JcrUtils; -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}. - */ -@Deprecated -public class JackrabbitContainer extends JackrabbitWrapper { - 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 dataModelMigrations = new HashSet(); - - /** 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 ArgeoJcrException("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 ArgeoJcrException("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 ArgeoJcrException("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( - dataModelMigrations)) { - if (dataModelMigration.migrate(session)) { - restartAndClearCaches = true; - } - } - } catch (ArgeoJcrException e) { - throw e; - } catch (Exception e) { - throw new ArgeoJcrException("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 ArgeoJcrException("Cannot login to migrated repository", e); - } - - for (JackrabbitDataModelMigration dataModelMigration : new TreeSet( - 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; - if (log.isTraceEnabled()) - log.trace("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 ArgeoJcrException("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 ArgeoJcrException("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 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(vars.keySet())) - log.trace(key + "=" + vars.getProperty(key.toString())); - } - - } catch (IOException e) { - throw new ArgeoJcrException("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 ArgeoJcrException("Cannot be used to wrap another repository"); - } - - public void setDataModelMigrations(Set 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; - } - -} diff --git a/org.argeo.server.jcr/src/org/argeo/jackrabbit/JackrabbitDataModelMigration.java b/org.argeo.server.jcr/src/org/argeo/jackrabbit/JackrabbitDataModelMigration.java deleted file mode 100644 index 53f0e4457..000000000 --- a/org.argeo.server.jcr/src/org/argeo/jackrabbit/JackrabbitDataModelMigration.java +++ /dev/null @@ -1,183 +0,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. - */ -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.jcr.ArgeoJcrException; -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 { - 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 ArgeoJcrException("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"; - // FIXME causes weird error in Eclipse - //repositoryConfig.getFileSystem().deleteFile(customeNodeTypesPath); - if (log.isDebugEnabled()) - log.debug("Cleared " + customeNodeTypesPath); - } catch (Exception e) { - throw new ArgeoJcrException("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; - } - -} diff --git a/org.argeo.server.jcr/src/org/argeo/jackrabbit/JackrabbitRepositoryFactory.java b/org.argeo.server.jcr/src/org/argeo/jackrabbit/JackrabbitRepositoryFactory.java deleted file mode 100644 index e4134883c..000000000 --- a/org.argeo.server.jcr/src/org/argeo/jackrabbit/JackrabbitRepositoryFactory.java +++ /dev/null @@ -1,178 +0,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. - */ -package org.argeo.jackrabbit; - -import java.io.File; -import java.io.InputStream; -import java.net.URI; -import java.net.URISyntaxException; -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.jcr.ArgeoJcrConstants; -import org.argeo.jcr.ArgeoJcrException; -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 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" }) - 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 - Repository repository; - 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)); - } else - throw new ArgeoJcrException("Unrecognized URI format " + uri); - - } - - else 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); - repository = getRepositoryByAlias(alias); - } else - throw new ArgeoJcrException("Not enough information in " + parameters); - - if (repository == null) - throw new ArgeoJcrException("Repository not found " + parameters); - - return repository; - } - - protected Repository getRepositoryByAlias(String alias) { - return null; - } - - protected Repository createRemoteRepository(String uri) throws RepositoryException { - Map params = new HashMap(); - params.put(JcrUtils.REPOSITORY_URI, uri); - Repository repository = new Jcr2davRepositoryFactory().getRepository(params); - if (repository == null) - throw new ArgeoJcrException("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 ArgeoJcrException("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 ArgeoJcrException("Cannot create repository " + uri, e); - } finally { - IOUtils.closeQuietly(configurationIn); - } - } - - protected String getAliasFromURI(String uri) { - try { - URI uriObj = new URI(uri); - String alias = uriObj.getPath(); - if (alias.charAt(0) == '/') - alias = alias.substring(1); - if (alias.charAt(alias.length() - 1) == '/') - alias = alias.substring(0, alias.length() - 1); - return alias; - } catch (URISyntaxException e) { - throw new ArgeoJcrException("Cannot interpret URI " + uri, e); - } - } - - /** - * 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; - } - -} diff --git a/org.argeo.server.jcr/src/org/argeo/jackrabbit/JackrabbitWrapper.java b/org.argeo.server.jcr/src/org/argeo/jackrabbit/JackrabbitWrapper.java deleted file mode 100644 index 268ecdb28..000000000 --- a/org.argeo.server.jcr/src/org/argeo/jackrabbit/JackrabbitWrapper.java +++ /dev/null @@ -1,397 +0,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. - */ -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.LoginException; -import javax.jcr.NoSuchWorkspaceException; -import javax.jcr.Node; -import javax.jcr.NodeIterator; -import javax.jcr.Repository; -import javax.jcr.RepositoryException; -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.api.JackrabbitRepository; -import org.apache.jackrabbit.commons.NamespaceHelper; -import org.apache.jackrabbit.commons.cnd.CndImporter; -import org.argeo.jcr.ArgeoJcrConstants; -import org.argeo.jcr.ArgeoJcrException; -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}. - */ -@Deprecated -public class JackrabbitWrapper extends JcrRepositoryWrapper implements - JackrabbitRepository, 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 cndFiles = new ArrayList(); - /** - * 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 namespaces = new HashMap(); - - 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 ArgeoJcrException("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.isTraceEnabled()) - log.trace("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 ArgeoJcrException("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 ArgeoJcrException("No " + resUrl + " in the classpath," - + " make sure the containing" + " package is visible."); - } - - return IOUtils.toByteArray(in); - } catch (Exception e) { - throw new ArgeoJcrException("Cannot read CND from " + resUrl, e); - } finally { - IOUtils.closeQuietly(in); - } - } - - /* - * JACKRABBIT REPOSITORY IMPLEMENTATION - */ - @Override - public Session login(Credentials credentials, String workspaceName, - Map attributes) throws LoginException, - NoSuchWorkspaceException, RepositoryException { - // TODO Auto-generated method stub - return null; - } - - @Override - public void shutdown() { - // TODO Auto-generated method stub - - } - - /* - * 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); - PackageAdmin packageAdmin = (PackageAdmin) bundleContext - .getService(paSr); - - // find exported package - ExportedPackage exportedPackage = null; - ExportedPackage[] exportedPackages = packageAdmin - .getExportedPackages(pkg); - if (exportedPackages == null) - throw new ArgeoJcrException("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 ArgeoJcrException("No OSGi exporting package found for " - // + resUrl); - } - return exportingBundle; - } - - /* - * FIELDS ACCESS - */ - public void setNamespaces(Map namespaces) { - this.namespaces = namespaces; - } - - public void setCndFiles(List 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; - } - -} diff --git a/org.argeo.server.jcr/src/org/argeo/jackrabbit/repository-h2.xml b/org.argeo.server.jcr/src/org/argeo/jackrabbit/repository-h2.xml deleted file mode 100644 index 05267621f..000000000 --- a/org.argeo.server.jcr/src/org/argeo/jackrabbit/repository-h2.xml +++ /dev/null @@ -1,82 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/org.argeo.server.jcr/src/org/argeo/jackrabbit/repository-localfs.xml b/org.argeo.server.jcr/src/org/argeo/jackrabbit/repository-localfs.xml deleted file mode 100644 index 3d2470863..000000000 --- a/org.argeo.server.jcr/src/org/argeo/jackrabbit/repository-localfs.xml +++ /dev/null @@ -1,60 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/org.argeo.server.jcr/src/org/argeo/jackrabbit/repository-memory.xml b/org.argeo.server.jcr/src/org/argeo/jackrabbit/repository-memory.xml deleted file mode 100644 index ecee5bdad..000000000 --- a/org.argeo.server.jcr/src/org/argeo/jackrabbit/repository-memory.xml +++ /dev/null @@ -1,55 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/org.argeo.server.jcr/src/org/argeo/jackrabbit/repository-postgresql-ds.xml b/org.argeo.server.jcr/src/org/argeo/jackrabbit/repository-postgresql-ds.xml deleted file mode 100644 index 07a0d0428..000000000 --- a/org.argeo.server.jcr/src/org/argeo/jackrabbit/repository-postgresql-ds.xml +++ /dev/null @@ -1,82 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/org.argeo.server.jcr/src/org/argeo/jackrabbit/repository-postgresql.xml b/org.argeo.server.jcr/src/org/argeo/jackrabbit/repository-postgresql.xml deleted file mode 100644 index 967782820..000000000 --- a/org.argeo.server.jcr/src/org/argeo/jackrabbit/repository-postgresql.xml +++ /dev/null @@ -1,79 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/org.argeo.server.jcr/src/org/argeo/jackrabbit/servlet/OpenInViewSessionProvider.java b/org.argeo.server.jcr/src/org/argeo/jackrabbit/servlet/OpenInViewSessionProvider.java deleted file mode 100644 index 52a9883ee..000000000 --- a/org.argeo.server.jcr/src/org/argeo/jackrabbit/servlet/OpenInViewSessionProvider.java +++ /dev/null @@ -1,70 +0,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. - */ -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 - */ -@Deprecated -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 ? "" : 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() { - } - -} diff --git a/org.argeo.server.jcr/src/org/argeo/jackrabbit/servlet/RemotingServlet.java b/org.argeo.server.jcr/src/org/argeo/jackrabbit/servlet/RemotingServlet.java deleted file mode 100644 index 3fdb5d2e1..000000000 --- a/org.argeo.server.jcr/src/org/argeo/jackrabbit/servlet/RemotingServlet.java +++ /dev/null @@ -1,52 +0,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. - */ -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 */ -@Deprecated -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; - public final static String INIT_PARAM_PROTECTED_HANDLERS_CONFIG = JcrRemotingServlet.INIT_PARAM_PROTECTED_HANDLERS_CONFIG; - - 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; - } - -} diff --git a/org.argeo.server.jcr/src/org/argeo/jackrabbit/servlet/WebdavServlet.java b/org.argeo.server.jcr/src/org/argeo/jackrabbit/servlet/WebdavServlet.java deleted file mode 100644 index e3176b742..000000000 --- a/org.argeo.server.jcr/src/org/argeo/jackrabbit/servlet/WebdavServlet.java +++ /dev/null @@ -1,63 +0,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. - */ -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 */ -@Deprecated -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; - } - -} diff --git a/org.argeo.server.jcr/src/org/argeo/jackrabbit/unit/AbstractJackrabbitTestCase.java b/org.argeo.server.jcr/src/org/argeo/jackrabbit/unit/AbstractJackrabbitTestCase.java deleted file mode 100644 index 1523c835d..000000000 --- a/org.argeo.server.jcr/src/org/argeo/jackrabbit/unit/AbstractJackrabbitTestCase.java +++ /dev/null @@ -1,66 +0,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. - */ -package org.argeo.jackrabbit.unit; - -import java.net.URL; - -import javax.jcr.Repository; - -import org.apache.commons.io.FileUtils; -import org.apache.jackrabbit.core.RepositoryImpl; -import org.apache.jackrabbit.core.config.RepositoryConfig; -import org.argeo.jcr.unit.AbstractJcrTestCase; - -/** Factorizes configuration of an in memory transient repository */ -public abstract class AbstractJackrabbitTestCase extends AbstractJcrTestCase { - protected RepositoryImpl repositoryImpl; - - // protected File getRepositoryFile() throws Exception { - // Resource res = new ClassPathResource( - // "org/argeo/jackrabbit/unit/repository-memory.xml"); - // return res.getFile(); - // } - - public AbstractJackrabbitTestCase() { - URL url = AbstractJackrabbitTestCase.class.getResource("jaas.config"); - assert url != null; - System.setProperty("java.security.auth.login.config", url.toString()); - } - - protected Repository createRepository() throws Exception { - // Repository repository = new TransientRepository(getRepositoryFile(), - // getHomeDir()); - RepositoryConfig repositoryConfig = RepositoryConfig.create( - AbstractJackrabbitTestCase.class - .getResourceAsStream(getRepositoryConfigResource()), - getHomeDir().getAbsolutePath()); - RepositoryImpl repositoryImpl = RepositoryImpl.create(repositoryConfig); - return repositoryImpl; - } - - protected String getRepositoryConfigResource() { - return "repository-memory.xml"; - } - - @Override - protected void clearRepository(Repository repository) throws Exception { - RepositoryImpl repositoryImpl = (RepositoryImpl) repository; - if (repositoryImpl != null) - repositoryImpl.shutdown(); - FileUtils.deleteDirectory(getHomeDir()); - } - -} diff --git a/org.argeo.server.jcr/src/org/argeo/jackrabbit/unit/jaas.config b/org.argeo.server.jcr/src/org/argeo/jackrabbit/unit/jaas.config deleted file mode 100644 index 11d2f96cf..000000000 --- a/org.argeo.server.jcr/src/org/argeo/jackrabbit/unit/jaas.config +++ /dev/null @@ -1,7 +0,0 @@ -TEST_JACKRABBIT_ADMIN { - org.argeo.jackrabbit.JackrabbitAdminLoginModule requisite; -}; - -Jackrabbit { - org.argeo.security.jackrabbit.SystemJackrabbitLoginModule requisite; -}; diff --git a/org.argeo.server.jcr/src/org/argeo/jackrabbit/unit/repository-h2.xml b/org.argeo.server.jcr/src/org/argeo/jackrabbit/unit/repository-h2.xml deleted file mode 100644 index 348dc288b..000000000 --- a/org.argeo.server.jcr/src/org/argeo/jackrabbit/unit/repository-h2.xml +++ /dev/null @@ -1,81 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/org.argeo.server.jcr/src/org/argeo/jackrabbit/unit/repository-memory.xml b/org.argeo.server.jcr/src/org/argeo/jackrabbit/unit/repository-memory.xml deleted file mode 100644 index 839542417..000000000 --- a/org.argeo.server.jcr/src/org/argeo/jackrabbit/unit/repository-memory.xml +++ /dev/null @@ -1,72 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/org.argeo.server.jcr/src/org/argeo/jcr/ArgeoJcrConstants.java b/org.argeo.server.jcr/src/org/argeo/jcr/ArgeoJcrConstants.java deleted file mode 100644 index 26979e927..000000000 --- a/org.argeo.server.jcr/src/org/argeo/jcr/ArgeoJcrConstants.java +++ /dev/null @@ -1,43 +0,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. - */ -package org.argeo.jcr; - -import javax.jcr.Repository; - -/** Argeo model specific constants */ -public interface ArgeoJcrConstants { - public final static String ARGEO_BASE_PATH = "/argeo:system"; - public final static String DATA_MODELS_BASE_PATH = ARGEO_BASE_PATH + "/argeo:dataModels"; - public final static String PEOPLE_BASE_PATH = ARGEO_BASE_PATH + "/argeo:people"; - - // parameters (typically for call to a RepositoryFactory) - /** Key for a JCR repository alias */ - public final static String JCR_REPOSITORY_ALIAS = "argeo.jcr.repository.alias"; - /** Key for a JCR repository URI */ - public final static String JCR_REPOSITORY_URI = "argeo.jcr.repository.uri"; - - // standard aliases - /** - * Reserved alias for the "node" {@link Repository}, that is, the default - * JCR repository. - */ - public final static String ALIAS_NODE = "node"; - public final static String ALIAS_HOME = "home"; - public final static String BASE_REPO_PID = "argeo.repo."; - public final static String REPO_PID_NODE = BASE_REPO_PID + ALIAS_NODE; - public final static String JACKRABBIT_REPO_FACTORY_PID = "argeo.repo.factory.jackrabbit"; - -} diff --git a/org.argeo.server.jcr/src/org/argeo/jcr/ArgeoJcrException.java b/org.argeo.server.jcr/src/org/argeo/jcr/ArgeoJcrException.java deleted file mode 100644 index 8e19593dd..000000000 --- a/org.argeo.server.jcr/src/org/argeo/jcr/ArgeoJcrException.java +++ /dev/null @@ -1,15 +0,0 @@ -package org.argeo.jcr; - -/** Argeo JCR specific exceptions. */ -public class ArgeoJcrException extends RuntimeException { - private static final long serialVersionUID = -1941940005390084331L; - - public ArgeoJcrException(String message, Throwable cause) { - super(message, cause); - } - - public ArgeoJcrException(String message) { - super(message); - } - -} diff --git a/org.argeo.server.jcr/src/org/argeo/jcr/ArgeoJcrUtils.java b/org.argeo.server.jcr/src/org/argeo/jcr/ArgeoJcrUtils.java deleted file mode 100644 index 9a0fd1971..000000000 --- a/org.argeo.server.jcr/src/org/argeo/jcr/ArgeoJcrUtils.java +++ /dev/null @@ -1,78 +0,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. - */ -package org.argeo.jcr; - -import java.util.HashMap; -import java.util.Map; - -import javax.jcr.Repository; -import javax.jcr.RepositoryException; -import javax.jcr.RepositoryFactory; - -/** Utilities related to Argeo model in JCR */ -public class ArgeoJcrUtils implements ArgeoJcrConstants { - /** - * Wraps the call to the repository factory based on parameter - * {@link ArgeoJcrConstants#JCR_REPOSITORY_ALIAS} in order to simplify it - * and protect against future API changes. - */ - public static Repository getRepositoryByAlias( - RepositoryFactory repositoryFactory, String alias) { - try { - Map parameters = new HashMap(); - parameters.put(JCR_REPOSITORY_ALIAS, alias); - return repositoryFactory.getRepository(parameters); - } catch (RepositoryException e) { - throw new ArgeoJcrException( - "Unexpected exception when trying to retrieve repository with alias " - + alias, e); - } - } - - /** - * Wraps the call to the repository factory based on parameter - * {@link ArgeoJcrConstants#JCR_REPOSITORY_URI} in order to simplify it and - * protect against future API changes. - */ - public static Repository getRepositoryByUri( - RepositoryFactory repositoryFactory, String uri) { - return getRepositoryByUri(repositoryFactory, uri, null); - } - - /** - * Wraps the call to the repository factory based on parameter - * {@link ArgeoJcrConstants#JCR_REPOSITORY_URI} in order to simplify it and - * protect against future API changes. - */ - public static Repository getRepositoryByUri( - RepositoryFactory repositoryFactory, String uri, String alias) { - try { - Map parameters = new HashMap(); - parameters.put(JCR_REPOSITORY_URI, uri); - if (alias != null) - parameters.put(JCR_REPOSITORY_ALIAS, alias); - return repositoryFactory.getRepository(parameters); - } catch (RepositoryException e) { - throw new ArgeoJcrException( - "Unexpected exception when trying to retrieve repository with uri " - + uri, e); - } - } - - private ArgeoJcrUtils() { - } - -} diff --git a/org.argeo.server.jcr/src/org/argeo/jcr/ArgeoNames.java b/org.argeo.server.jcr/src/org/argeo/jcr/ArgeoNames.java deleted file mode 100644 index 1d4582a78..000000000 --- a/org.argeo.server.jcr/src/org/argeo/jcr/ArgeoNames.java +++ /dev/null @@ -1,59 +0,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. - */ -package org.argeo.jcr; - -/** JCR names in the http://www.argeo.org/argeo namespace */ -public interface ArgeoNames { - public final static String ARGEO_NAMESPACE = "http://www.argeo.org/ns/argeo"; - public final static String ARGEO = "argeo"; - - public final static String ARGEO_URI = "argeo:uri"; - public final static String ARGEO_USER_ID = "argeo:userID"; - public final static String ARGEO_PREFERENCES = "argeo:preferences"; - public final static String ARGEO_DATA_MODEL_VERSION = "argeo:dataModelVersion"; - - public final static String ARGEO_REMOTE = "argeo:remote"; - public final static String ARGEO_PASSWORD = "argeo:password"; -// public final static String ARGEO_REMOTE_ROLES = "argeo:remoteRoles"; - - // user profile - public final static String ARGEO_PROFILE = "argeo:profile"; - - // spring security - public final static String ARGEO_ENABLED = "argeo:enabled"; - public final static String ARGEO_ACCOUNT_NON_EXPIRED = "argeo:accountNonExpired"; - public final static String ARGEO_ACCOUNT_NON_LOCKED = "argeo:accountNonLocked"; - public final static String ARGEO_CREDENTIALS_NON_EXPIRED = "argeo:credentialsNonExpired"; - - // personal details - public final static String ARGEO_FIRST_NAME = "argeo:firstName"; - public final static String ARGEO_LAST_NAME = "argeo:lastName"; - public final static String ARGEO_PRIMARY_EMAIL = "argeo:primaryEmail"; - public final static String ARGEO_PRIMARY_ORGANIZATION = "argeo:primaryOrganization"; - - // tabular - public final static String ARGEO_IS_KEY = "argeo:isKey"; - - // crypto - public final static String ARGEO_IV = "argeo:iv"; - public final static String ARGEO_SECRET_KEY_FACTORY = "argeo:secretKeyFactory"; - public final static String ARGEO_SALT = "argeo:salt"; - public final static String ARGEO_ITERATION_COUNT = "argeo:iterationCount"; - public final static String ARGEO_KEY_LENGTH = "argeo:keyLength"; - public final static String ARGEO_SECRET_KEY_ENCRYPTION = "argeo:secretKeyEncryption"; - public final static String ARGEO_CIPHER = "argeo:cipher"; - public final static String ARGEO_KEYRING = "argeo:keyring"; -} diff --git a/org.argeo.server.jcr/src/org/argeo/jcr/ArgeoTypes.java b/org.argeo.server.jcr/src/org/argeo/jcr/ArgeoTypes.java deleted file mode 100644 index a11ead53f..000000000 --- a/org.argeo.server.jcr/src/org/argeo/jcr/ArgeoTypes.java +++ /dev/null @@ -1,38 +0,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. - */ -package org.argeo.jcr; - -/** JCR types in the http://www.argeo.org/argeo namespace */ -public interface ArgeoTypes { - public final static String ARGEO_LINK = "argeo:link"; - public final static String ARGEO_USER_HOME = "argeo:userHome"; - public final static String ARGEO_USER_PROFILE = "argeo:userProfile"; - public final static String ARGEO_REMOTE_REPOSITORY = "argeo:remoteRepository"; - public final static String ARGEO_PREFERENCE_NODE = "argeo:preferenceNode"; - - // data model - public final static String ARGEO_DATA_MODEL = "argeo:dataModel"; - - // tabular - public final static String ARGEO_TABLE = "argeo:table"; - public final static String ARGEO_COLUMN = "argeo:column"; - public final static String ARGEO_CSV = "argeo:csv"; - - // crypto - public final static String ARGEO_ENCRYPTED = "argeo:encrypted"; - public final static String ARGEO_PBE_SPEC = "argeo:pbeSpec"; - -} diff --git a/org.argeo.server.jcr/src/org/argeo/jcr/CollectionNodeIterator.java b/org.argeo.server.jcr/src/org/argeo/jcr/CollectionNodeIterator.java deleted file mode 100644 index a65907a7a..000000000 --- a/org.argeo.server.jcr/src/org/argeo/jcr/CollectionNodeIterator.java +++ /dev/null @@ -1,76 +0,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. - */ -package org.argeo.jcr; - -import java.util.Collection; -import java.util.Iterator; -import java.util.NoSuchElementException; - -import javax.jcr.Node; -import javax.jcr.NodeIterator; - -/** Wraps a collection of nodes in order to read it as a {@link NodeIterator} */ -public class CollectionNodeIterator implements NodeIterator { - private final Long collectionSize; - private final Iterator iterator; - private Integer position = 0; - - public CollectionNodeIterator(Collection nodes) { - super(); - this.collectionSize = (long) nodes.size(); - this.iterator = nodes.iterator(); - } - - public void skip(long skipNum) { - if (skipNum < 0) - throw new IllegalArgumentException( - "Skip count has to be positive: " + skipNum); - - for (long i = 0; i < skipNum; i++) { - if (!hasNext()) - throw new NoSuchElementException("Last element past (position=" - + getPosition() + ")"); - nextNode(); - } - } - - public long getSize() { - return collectionSize; - } - - public long getPosition() { - return position; - } - - public boolean hasNext() { - return iterator.hasNext(); - } - - public Object next() { - return nextNode(); - } - - public void remove() { - iterator.remove(); - } - - public Node nextNode() { - Node node = iterator.next(); - position++; - return node; - } - -} diff --git a/org.argeo.server.jcr/src/org/argeo/jcr/DefaultJcrListener.java b/org.argeo.server.jcr/src/org/argeo/jcr/DefaultJcrListener.java deleted file mode 100644 index 5ef8edd56..000000000 --- a/org.argeo.server.jcr/src/org/argeo/jcr/DefaultJcrListener.java +++ /dev/null @@ -1,93 +0,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. - */ -package org.argeo.jcr; - -import javax.jcr.RepositoryException; -import javax.jcr.Session; -import javax.jcr.observation.Event; -import javax.jcr.observation.EventIterator; -import javax.jcr.observation.EventListener; -import javax.jcr.observation.ObservationManager; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - -/** To be overridden */ -public class DefaultJcrListener implements EventListener { - private final static Log log = LogFactory.getLog(DefaultJcrListener.class); - private Session session; - private String path = "/"; - private Boolean deep = true; - - public void start() { - try { - addEventListener(session().getWorkspace().getObservationManager()); - if (log.isDebugEnabled()) - log.debug("Registered JCR event listener on " + path); - } catch (Exception e) { - throw new ArgeoJcrException("Cannot register event listener", e); - } - } - - public void stop() { - try { - session().getWorkspace().getObservationManager() - .removeEventListener(this); - if (log.isDebugEnabled()) - log.debug("Unregistered JCR event listener on " + path); - } catch (Exception e) { - throw new ArgeoJcrException("Cannot unregister event listener", e); - } - } - - /** Default is listen to all events */ - protected Integer getEvents() { - return Event.NODE_ADDED | Event.NODE_REMOVED | Event.PROPERTY_ADDED - | Event.PROPERTY_CHANGED | Event.PROPERTY_REMOVED; - } - - /** To be overidden */ - public void onEvent(EventIterator events) { - while (events.hasNext()) { - Event event = events.nextEvent(); - log.debug(event); - } - } - - /** To be overidden */ - protected void addEventListener(ObservationManager observationManager) - throws RepositoryException { - observationManager.addEventListener(this, getEvents(), path, deep, - null, null, false); - } - - private Session session() { - return session; - } - - public void setPath(String path) { - this.path = path; - } - - public void setDeep(Boolean deep) { - this.deep = deep; - } - - public void setSession(Session session) { - this.session = session; - } - -} diff --git a/org.argeo.server.jcr/src/org/argeo/jcr/DefaultRepositoryFactory.java b/org.argeo.server.jcr/src/org/argeo/jcr/DefaultRepositoryFactory.java deleted file mode 100644 index ccfc2698e..000000000 --- a/org.argeo.server.jcr/src/org/argeo/jcr/DefaultRepositoryFactory.java +++ /dev/null @@ -1,76 +0,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. - */ -package org.argeo.jcr; - -import java.net.URI; -import java.net.URISyntaxException; -import java.util.Map; -import java.util.Properties; - -import javax.jcr.Repository; -import javax.jcr.RepositoryException; -import javax.jcr.RepositoryFactory; - -/** - * Simple implementation of {@link RepositoryFactory}, supporting OSGi aliases. - */ -@Deprecated -public class DefaultRepositoryFactory extends DefaultRepositoryRegister - implements RepositoryFactory, ArgeoJcrConstants { - @SuppressWarnings("rawtypes") - public Repository getRepository(Map parameters) throws RepositoryException { - if (parameters.containsKey(JCR_REPOSITORY_ALIAS)) { - String alias = parameters.get(JCR_REPOSITORY_ALIAS).toString(); - return getRepositoryByAlias(alias); - } else if (parameters.containsKey(JCR_REPOSITORY_URI)) { - String uri = parameters.get(JCR_REPOSITORY_URI).toString(); - return getRepositoryByAlias(getAliasFromURI(uri)); - } - return null; - } - - protected String getAliasFromURI(String uri) { - try { - URI uriObj = new URI(uri); - String alias = uriObj.getPath(); - if (alias.charAt(0) == '/') - alias = alias.substring(1); - if (alias.charAt(alias.length() - 1) == '/') - alias = alias.substring(0, alias.length() - 1); - return alias; - } catch (URISyntaxException e) { - throw new ArgeoJcrException("Cannot interpret URI " + uri, e); - } - } - - /** - * Retrieve a repository by alias - * - * @return the repository registered with alias or null if none - */ - protected Repository getRepositoryByAlias(String alias) { - if (getRepositories().containsKey(alias)) - return getRepositories().get(alias); - else - return null; - } - - protected void publish(String alias, Repository repository, - Properties properties) { - register(repository, properties); - } - -} diff --git a/org.argeo.server.jcr/src/org/argeo/jcr/DefaultRepositoryRegister.java b/org.argeo.server.jcr/src/org/argeo/jcr/DefaultRepositoryRegister.java deleted file mode 100644 index e2a5026d8..000000000 --- a/org.argeo.server.jcr/src/org/argeo/jcr/DefaultRepositoryRegister.java +++ /dev/null @@ -1,97 +0,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. - */ -package org.argeo.jcr; - -import java.util.Collections; -import java.util.Map; -import java.util.Observable; -import java.util.TreeMap; - -import javax.jcr.Repository; -import javax.jcr.RepositoryException; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - -@Deprecated -public class DefaultRepositoryRegister extends Observable implements - RepositoryRegister, ArgeoJcrConstants { - private final static Log log = LogFactory - .getLog(DefaultRepositoryRegister.class); - - /** Read only map which will be directly exposed. */ - private Map repositories = Collections - .unmodifiableMap(new TreeMap()); - - @SuppressWarnings("rawtypes") - public synchronized Repository getRepository(Map parameters) - throws RepositoryException { - if (!parameters.containsKey(JCR_REPOSITORY_ALIAS)) - throw new RepositoryException("Parameter " + JCR_REPOSITORY_ALIAS - + " has to be defined."); - String alias = parameters.get(JCR_REPOSITORY_ALIAS).toString(); - if (!repositories.containsKey(alias)) - throw new RepositoryException( - "No repository registered with alias " + alias); - - return repositories.get(alias); - } - - /** Access to the read-only map */ - public synchronized Map getRepositories() { - return repositories; - } - - /** Registers a service, typically called when OSGi services are bound. */ - @SuppressWarnings("rawtypes") - public synchronized void register(Repository repository, Map properties) { - String alias; - if (properties == null || !properties.containsKey(JCR_REPOSITORY_ALIAS)) { - log.warn("Cannot register a repository if no " - + JCR_REPOSITORY_ALIAS + " property is specified."); - return; - } - alias = properties.get(JCR_REPOSITORY_ALIAS).toString(); - Map map = new TreeMap( - repositories); - map.put(alias, repository); - repositories = Collections.unmodifiableMap(map); - setChanged(); - notifyObservers(alias); - } - - /** Unregisters a service, typically called when OSGi services are unbound. */ - @SuppressWarnings("rawtypes") - public synchronized void unregister(Repository repository, Map properties) { - // TODO: also check bean name? - if (properties == null || !properties.containsKey(JCR_REPOSITORY_ALIAS)) { - log.warn("Cannot unregister a repository without property " - + JCR_REPOSITORY_ALIAS); - return; - } - - String alias = properties.get(JCR_REPOSITORY_ALIAS).toString(); - Map map = new TreeMap( - repositories); - if (map.remove(alias) == null) { - log.warn("No repository was registered with alias " + alias); - return; - } - repositories = Collections.unmodifiableMap(map); - setChanged(); - notifyObservers(alias); - } -} diff --git a/org.argeo.server.jcr/src/org/argeo/jcr/JcrCallback.java b/org.argeo.server.jcr/src/org/argeo/jcr/JcrCallback.java deleted file mode 100644 index 0c4706f84..000000000 --- a/org.argeo.server.jcr/src/org/argeo/jcr/JcrCallback.java +++ /dev/null @@ -1,23 +0,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. - */ -package org.argeo.jcr; - -import javax.jcr.Session; - -/** An arbitrary execution on a JCR session, optionally returning a result. */ -public interface JcrCallback { - public Object execute(Session session); -} diff --git a/org.argeo.server.jcr/src/org/argeo/jcr/JcrMonitor.java b/org.argeo.server.jcr/src/org/argeo/jcr/JcrMonitor.java deleted file mode 100644 index f04be9aa5..000000000 --- a/org.argeo.server.jcr/src/org/argeo/jcr/JcrMonitor.java +++ /dev/null @@ -1,89 +0,0 @@ -package org.argeo.jcr; - -import org.argeo.ArgeoMonitor; - -/** - * Simple monitor abstraction. Inspired by Eclipse IProgressMOnitor, but without - * dependency to it. - */ -@SuppressWarnings("deprecation") -public interface JcrMonitor extends ArgeoMonitor { - /** - * Constant indicating an unknown amount of work. - */ - public final static int UNKNOWN = -1; - - /** - * Notifies that the main task is beginning. This must only be called once - * on a given progress monitor instance. - * - * @param name - * the name (or description) of the main task - * @param totalWork - * the total number of work units into which the main task is - * been subdivided. If the value is UNKNOWN the - * implementation is free to indicate progress in a way which - * doesn't require the total number of work units in advance. - */ - public void beginTask(String name, int totalWork); - - /** - * Notifies that the work is done; that is, either the main task is - * completed or the user canceled it. This method may be called more than - * once (implementations should be prepared to handle this case). - */ - public void done(); - - /** - * Returns whether cancelation of current operation has been requested. - * Long-running operations should poll to see if cancelation has been - * requested. - * - * @return true if cancellation has been requested, and - * false otherwise - * @see #setCanceled(boolean) - */ - public boolean isCanceled(); - - /** - * Sets the cancel state to the given value. - * - * @param value - * true indicates that cancelation has been - * requested (but not necessarily acknowledged); - * false clears this flag - * @see #isCanceled() - */ - public void setCanceled(boolean value); - - /** - * Sets the task name to the given value. This method is used to restore the - * task label after a nested operation was executed. Normally there is no - * need for clients to call this method. - * - * @param name - * the name (or description) of the main task - * @see #beginTask(java.lang.String, int) - */ - public void setTaskName(String name); - - /** - * Notifies that a subtask of the main task is beginning. Subtasks are - * optional; the main task might not have subtasks. - * - * @param name - * the name (or description) of the subtask - */ - public void subTask(String name); - - /** - * Notifies that a given number of work unit of the main task has been - * completed. Note that this amount represents an installment, as opposed to - * a cumulative amount of work done to date. - * - * @param work - * a non-negative number of work units just completed - */ - public void worked(int work); - -} diff --git a/org.argeo.server.jcr/src/org/argeo/jcr/JcrRepositoryWrapper.java b/org.argeo.server.jcr/src/org/argeo/jcr/JcrRepositoryWrapper.java deleted file mode 100644 index 6c23acaac..000000000 --- a/org.argeo.server.jcr/src/org/argeo/jcr/JcrRepositoryWrapper.java +++ /dev/null @@ -1,150 +0,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. - */ -package org.argeo.jcr; - -import javax.jcr.Credentials; -import javax.jcr.LoginException; -import javax.jcr.NoSuchWorkspaceException; -import javax.jcr.Repository; -import javax.jcr.RepositoryException; -import javax.jcr.Session; -import javax.jcr.Value; - -/** - * Wrapper around a JCR repository which allows to simplify configuration and - * intercept some actions. It exposes itself as a {@link Repository}. - */ -public abstract class JcrRepositoryWrapper implements Repository { - // private final static Log log = LogFactory - // .getLog(JcrRepositoryWrapper.class); - - // wrapped repository - private Repository repository; - - private Boolean autocreateWorkspaces = false; - - /** - * Empty constructor, {@link #init()} should be called after properties have - * been set - */ - public JcrRepositoryWrapper() { - } - - /** Initializes */ - public void init() { - } - - /** Shutdown the repository */ - public void destroy() throws Exception { - } - - /* - * DELEGATED JCR REPOSITORY METHODS - */ - - public String getDescriptor(String key) { - return getRepository().getDescriptor(key); - } - - public String[] getDescriptorKeys() { - return getRepository().getDescriptorKeys(); - } - - /** Central login method */ - public Session login(Credentials credentials, String workspaceName) - throws LoginException, NoSuchWorkspaceException, - RepositoryException { - Session session; - try { - session = getRepository().login(credentials, workspaceName); - } catch (NoSuchWorkspaceException e) { - if (autocreateWorkspaces && workspaceName != null) - session = createWorkspaceAndLogsIn(credentials, workspaceName); - else - throw e; - } - processNewSession(session); - return session; - } - - public Session login() throws LoginException, RepositoryException { - return login(null, null); - } - - public Session login(Credentials credentials) throws LoginException, - RepositoryException { - return login(credentials, null); - } - - public Session login(String workspaceName) throws LoginException, - NoSuchWorkspaceException, RepositoryException { - return login(null, workspaceName); - } - - /** Called after a session has been created, does nothing by default. */ - protected void processNewSession(Session session) { - } - - /** Wraps access to the repository, making sure it is available. */ - protected synchronized Repository getRepository() { -// if (repository == null) { -// throw new ArgeoJcrException("No repository initialized." -// + " Was the init() method called?" -// + " The destroy() method should also" -// + " be called on shutdown."); -// } - return repository; - } - - /** - * Logs in to the default workspace, creates the required workspace, logs - * out, logs in to the required workspace. - */ - protected Session createWorkspaceAndLogsIn(Credentials credentials, - String workspaceName) throws RepositoryException { - if (workspaceName == null) - throw new ArgeoJcrException("No workspace specified."); - Session session = getRepository().login(credentials); - session.getWorkspace().createWorkspace(workspaceName); - session.logout(); - return getRepository().login(credentials, workspaceName); - } - - public boolean isStandardDescriptor(String key) { - return getRepository().isStandardDescriptor(key); - } - - public boolean isSingleValueDescriptor(String key) { - return getRepository().isSingleValueDescriptor(key); - } - - public Value getDescriptorValue(String key) { - return getRepository().getDescriptorValue(key); - } - - public Value[] getDescriptorValues(String key) { - return getRepository().getDescriptorValues(key); - } - - public synchronized void setRepository(Repository repository) { - this.repository = repository; - } - - public void setAutocreateWorkspaces(Boolean autocreateWorkspaces) { - this.autocreateWorkspaces = autocreateWorkspaces; - } - -} diff --git a/org.argeo.server.jcr/src/org/argeo/jcr/JcrResourceAdapter.java b/org.argeo.server.jcr/src/org/argeo/jcr/JcrResourceAdapter.java deleted file mode 100644 index 1ccce4f76..000000000 --- a/org.argeo.server.jcr/src/org/argeo/jcr/JcrResourceAdapter.java +++ /dev/null @@ -1,244 +0,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. - */ -package org.argeo.jcr; - -import java.io.InputStream; -import java.util.ArrayList; -import java.util.Calendar; -import java.util.List; - -import javax.jcr.Binary; -import javax.jcr.Node; -import javax.jcr.Property; -import javax.jcr.RepositoryException; -import javax.jcr.Session; -import javax.jcr.nodetype.NodeType; -import javax.jcr.version.Version; -import javax.jcr.version.VersionHistory; -import javax.jcr.version.VersionIterator; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - -/** - * Bridge Spring resources and JCR folder / files semantics (nt:folder / - * nt:file), supporting versioning as well. - */ -public class JcrResourceAdapter { - private final static Log log = LogFactory.getLog(JcrResourceAdapter.class); - - private Session session; - - private Boolean versioning = true; - private String defaultEncoding = "UTF-8"; - - // private String restoreBase = "/.restore"; - - public JcrResourceAdapter() { - } - - public JcrResourceAdapter(Session session) { - this.session = session; - } - - public void mkdirs(String path) { - JcrUtils.mkdirs(session(), path, NodeType.NT_FOLDER, - NodeType.NT_FOLDER, versioning); - } - - public void create(String path, InputStream in, String mimeType) { - try { - if (session().itemExists(path)) { - throw new ArgeoJcrException("Node " + path + " already exists."); - } - - int index = path.lastIndexOf('/'); - String parentPath = path.substring(0, index); - if (parentPath.equals("")) - parentPath = "/"; - String fileName = path.substring(index + 1); - if (!session().itemExists(parentPath)) - throw new ArgeoJcrException("Parent folder of node " + path - + " does not exist: " + parentPath); - - Node folderNode = (Node) session().getItem(parentPath); - Node fileNode = folderNode.addNode(fileName, "nt:file"); - - Node contentNode = fileNode.addNode(Property.JCR_CONTENT, - "nt:resource"); - if (mimeType != null) - contentNode.setProperty(Property.JCR_MIMETYPE, mimeType); - contentNode.setProperty(Property.JCR_ENCODING, defaultEncoding); - Binary binary = session().getValueFactory().createBinary(in); - contentNode.setProperty(Property.JCR_DATA, binary); - JcrUtils.closeQuietly(binary); - Calendar lastModified = Calendar.getInstance(); - // lastModified.setTimeInMillis(file.lastModified()); - contentNode.setProperty(Property.JCR_LAST_MODIFIED, lastModified); - // resNode.addMixin("mix:referenceable"); - - if (versioning) - fileNode.addMixin("mix:versionable"); - - session().save(); - - if (versioning) - session().getWorkspace().getVersionManager() - .checkin(fileNode.getPath()); - - if (log.isDebugEnabled()) - log.debug("Created " + path); - } catch (Exception e) { - throw new ArgeoJcrException("Cannot create node for " + path, e); - } - - } - - public void update(String path, InputStream in) { - try { - - if (!session().itemExists(path)) { - String type = null; - // FIXME: using javax.activation leads to conflict between Java - // 1.5 and 1.6 (since javax.activation was included in Java 1.6) - // String type = new MimetypesFileTypeMap() - // .getContentType(FilenameUtils.getName(path)); - create(path, in, type); - return; - } - - Node fileNode = (Node) session().getItem(path); - Node contentNode = fileNode.getNode(Property.JCR_CONTENT); - if (versioning) - session().getWorkspace().getVersionManager() - .checkout(fileNode.getPath()); - Binary binary = session().getValueFactory().createBinary(in); - contentNode.setProperty(Property.JCR_DATA, binary); - JcrUtils.closeQuietly(binary); - Calendar lastModified = Calendar.getInstance(); - // lastModified.setTimeInMillis(file.lastModified()); - contentNode.setProperty(Property.JCR_LAST_MODIFIED, lastModified); - - session().save(); - if (versioning) - session().getWorkspace().getVersionManager() - .checkin(fileNode.getPath()); - - if (log.isDebugEnabled()) - log.debug("Updated " + path); - } catch (Exception e) { - throw new ArgeoJcrException("Cannot update node " + path, e); - } - } - - public List listVersions(String path) { - if (!versioning) - throw new ArgeoJcrException("Versioning is not activated"); - - try { - List versions = new ArrayList(); - Node fileNode = (Node) session().getItem(path); - VersionHistory history = session().getWorkspace() - .getVersionManager().getVersionHistory(fileNode.getPath()); - for (VersionIterator it = history.getAllVersions(); it.hasNext();) { - Version version = (Version) it.next(); - versions.add(version.getCreated()); - if (log.isTraceEnabled()) { - log.debug(version); - // debug(version); - } - } - return versions; - } catch (Exception e) { - throw new ArgeoJcrException("Cannot list version of node " + path, e); - } - } - - public InputStream retrieve(String path) { - try { - Node node = (Node) session().getItem( - path + "/" + Property.JCR_CONTENT); - Property property = node.getProperty(Property.JCR_DATA); - return property.getBinary().getStream(); - } catch (Exception e) { - throw new ArgeoJcrException("Cannot retrieve " + path, e); - } - } - - public synchronized InputStream retrieve(String path, Integer revision) { - if (!versioning) - throw new ArgeoJcrException("Versioning is not activated"); - - try { - Node fileNode = (Node) session().getItem(path); - VersionHistory history = session().getWorkspace() - .getVersionManager().getVersionHistory(fileNode.getPath()); - int count = 0; - Version version = null; - for (VersionIterator it = history.getAllVersions(); it.hasNext();) { - version = (Version) it.next(); - if (count == revision + 1) { - InputStream in = fromVersion(version); - if (log.isDebugEnabled()) - log.debug("Retrieved " + path + " at revision " - + revision); - return in; - } - count++; - } - } catch (Exception e) { - throw new ArgeoJcrException("Cannot retrieve version " + revision - + " of " + path, e); - } - - throw new ArgeoJcrException("Version " + revision - + " does not exist for node " + path); - } - - protected InputStream fromVersion(Version version) - throws RepositoryException { - Node frozenNode = version.getNode("jcr:frozenNode"); - InputStream in = frozenNode.getNode(Property.JCR_CONTENT) - .getProperty(Property.JCR_DATA).getBinary().getStream(); - return in; - } - - protected Session session() { - return session; - } - - public void setVersioning(Boolean versioning) { - this.versioning = versioning; - } - - public void setDefaultEncoding(String defaultEncoding) { - this.defaultEncoding = defaultEncoding; - } - - protected String fill(Integer number) { - int size = 4; - String str = number.toString(); - for (int i = str.length(); i < size; i++) { - str = "0" + str; - } - return str; - } - - public void setSession(Session session) { - this.session = session; - } - -} diff --git a/org.argeo.server.jcr/src/org/argeo/jcr/JcrUrlStreamHandler.java b/org.argeo.server.jcr/src/org/argeo/jcr/JcrUrlStreamHandler.java deleted file mode 100644 index a77763937..000000000 --- a/org.argeo.server.jcr/src/org/argeo/jcr/JcrUrlStreamHandler.java +++ /dev/null @@ -1,85 +0,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. - */ -package org.argeo.jcr; - -import java.io.IOException; -import java.io.InputStream; -import java.net.URL; -import java.net.URLConnection; -import java.net.URLStreamHandler; - -import javax.jcr.Item; -import javax.jcr.Node; -import javax.jcr.Property; -import javax.jcr.PropertyType; -import javax.jcr.RepositoryException; -import javax.jcr.Session; -import javax.jcr.nodetype.NodeType; - -/** URL stream handler able to deal with nt:file node and properties. NOT FINISHED */ -public class JcrUrlStreamHandler extends URLStreamHandler { - private final Session session; - - public JcrUrlStreamHandler(Session session) { - this.session = session; - } - - @Override - protected URLConnection openConnection(final URL u) throws IOException { - // TODO Auto-generated method stub - return new URLConnection(u) { - - @Override - public void connect() throws IOException { - String itemPath = u.getPath(); - try { - if (!session.itemExists(itemPath)) - throw new IOException("No item under " + itemPath); - - Item item = session.getItem(u.getPath()); - if (item.isNode()) { - // this should be a nt:file node - Node node = (Node) item; - if (!node.getPrimaryNodeType().isNodeType( - NodeType.NT_FILE)) - throw new IOException("Node " + node + " is not a " - + NodeType.NT_FILE); - - } else { - Property property = (Property) item; - if(property.getType()==PropertyType.BINARY){ - //Binary binary = property.getBinary(); - - } - } - } catch (RepositoryException e) { - IOException ioe = new IOException( - "Unexpected JCR exception"); - ioe.initCause(e); - throw ioe; - } - } - - @Override - public InputStream getInputStream() throws IOException { - // TODO Auto-generated method stub - return super.getInputStream(); - } - - }; - } - -} diff --git a/org.argeo.server.jcr/src/org/argeo/jcr/JcrUtils.java b/org.argeo.server.jcr/src/org/argeo/jcr/JcrUtils.java deleted file mode 100644 index 98a2da8f3..000000000 --- a/org.argeo.server.jcr/src/org/argeo/jcr/JcrUtils.java +++ /dev/null @@ -1,1592 +0,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. - */ -package org.argeo.jcr; - -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.File; -import java.io.FileInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.net.MalformedURLException; -import java.net.URL; -import java.security.Principal; -import java.text.DateFormat; -import java.text.ParseException; -import java.util.ArrayList; -import java.util.Calendar; -import java.util.Collections; -import java.util.Date; -import java.util.GregorianCalendar; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.TreeMap; - -import javax.jcr.Binary; -import javax.jcr.NamespaceRegistry; -import javax.jcr.NoSuchWorkspaceException; -import javax.jcr.Node; -import javax.jcr.NodeIterator; -import javax.jcr.Property; -import javax.jcr.PropertyIterator; -import javax.jcr.PropertyType; -import javax.jcr.Repository; -import javax.jcr.RepositoryException; -import javax.jcr.Session; -import javax.jcr.Value; -import javax.jcr.Workspace; -import javax.jcr.nodetype.NodeType; -import javax.jcr.observation.EventListener; -import javax.jcr.query.Query; -import javax.jcr.query.QueryResult; -import javax.jcr.security.AccessControlEntry; -import javax.jcr.security.AccessControlList; -import javax.jcr.security.AccessControlManager; -import javax.jcr.security.AccessControlPolicy; -import javax.jcr.security.AccessControlPolicyIterator; -import javax.jcr.security.Privilege; - -import org.apache.commons.io.IOUtils; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.argeo.ArgeoMonitor; -import org.argeo.util.security.DigestUtils; -import org.argeo.util.security.SimplePrincipal; - -/** Utility methods to simplify common JCR operations. */ -public class JcrUtils implements ArgeoJcrConstants { - - final private static Log log = LogFactory.getLog(JcrUtils.class); - - /** - * Not complete yet. See - * http://www.day.com/specs/jcr/2.0/3_Repository_Model.html#3.2.2%20Local - * %20Names - */ - public final static char[] INVALID_NAME_CHARACTERS = { '/', ':', '[', ']', - '|', '*', /* - * invalid XML chars : - */ - '<', '>', '&' }; - - /** Prevents instantiation */ - private JcrUtils() { - } - - /** - * Queries one single node. - * - * @return one single node or null if none was found - * @throws ArgeoJcrException - * if more than one node was found - */ - public static Node querySingleNode(Query query) { - NodeIterator nodeIterator; - try { - QueryResult queryResult = query.execute(); - nodeIterator = queryResult.getNodes(); - } catch (RepositoryException e) { - throw new ArgeoJcrException("Cannot execute query " + query, e); - } - Node node; - if (nodeIterator.hasNext()) - node = nodeIterator.nextNode(); - else - return null; - - if (nodeIterator.hasNext()) - throw new ArgeoJcrException("Query returned more than one node."); - return node; - } - - /** Retrieves the node name from the provided path */ - public static String nodeNameFromPath(String path) { - if (path.equals("/")) - return ""; - if (path.charAt(0) != '/') - throw new ArgeoJcrException("Path " + path + " must start with a '/'"); - String pathT = path; - if (pathT.charAt(pathT.length() - 1) == '/') - pathT = pathT.substring(0, pathT.length() - 2); - - int index = pathT.lastIndexOf('/'); - return pathT.substring(index + 1); - } - - /** Retrieves the parent path of the provided path */ - public static String parentPath(String path) { - if (path.equals("/")) - throw new ArgeoJcrException("Root path '/' has no parent path"); - if (path.charAt(0) != '/') - throw new ArgeoJcrException("Path " + path + " must start with a '/'"); - String pathT = path; - if (pathT.charAt(pathT.length() - 1) == '/') - pathT = pathT.substring(0, pathT.length() - 2); - - int index = pathT.lastIndexOf('/'); - return pathT.substring(0, index); - } - - /** The provided data as a path ('/' at the end, not the beginning) */ - public static String dateAsPath(Calendar cal) { - return dateAsPath(cal, false); - } - - /** - * Creates a deep path based on a URL: - * http://subdomain.example.com/to/content?args => - * com/example/subdomain/to/content - */ - public static String urlAsPath(String url) { - try { - URL u = new URL(url); - StringBuffer path = new StringBuffer(url.length()); - // invert host - path.append(hostAsPath(u.getHost())); - // we don't put port since it may not always be there and may change - path.append(u.getPath()); - return path.toString(); - } catch (MalformedURLException e) { - throw new ArgeoJcrException("Cannot generate URL path for " + url, e); - } - } - - /** Set the {@link NodeType#NT_ADDRESS} properties based on this URL. */ - public static void urlToAddressProperties(Node node, String url) { - try { - URL u = new URL(url); - node.setProperty(Property.JCR_PROTOCOL, u.getProtocol()); - node.setProperty(Property.JCR_HOST, u.getHost()); - node.setProperty(Property.JCR_PORT, Integer.toString(u.getPort())); - node.setProperty(Property.JCR_PATH, normalizePath(u.getPath())); - } catch (Exception e) { - throw new ArgeoJcrException("Cannot set URL " + url - + " as nt:address properties", e); - } - } - - /** Build URL based on the {@link NodeType#NT_ADDRESS} properties. */ - public static String urlFromAddressProperties(Node node) { - try { - URL u = new URL( - node.getProperty(Property.JCR_PROTOCOL).getString(), node - .getProperty(Property.JCR_HOST).getString(), - (int) node.getProperty(Property.JCR_PORT).getLong(), node - .getProperty(Property.JCR_PATH).getString()); - return u.toString(); - } catch (Exception e) { - throw new ArgeoJcrException( - "Cannot get URL from nt:address properties of " + node, e); - } - } - - /* - * PATH UTILITIES - */ - - /** Make sure that: starts with '/', do not end with '/', do not have '//' */ - public static String normalizePath(String path) { - List tokens = tokenize(path); - StringBuffer buf = new StringBuffer(path.length()); - for (String token : tokens) { - buf.append('/'); - buf.append(token); - } - return buf.toString(); - } - - /** - * Creates a path from a FQDN, inverting the order of the component: - * www.argeo.org => org.argeo.www - */ - public static String hostAsPath(String host) { - StringBuffer path = new StringBuffer(host.length()); - String[] hostTokens = host.split("\\."); - for (int i = hostTokens.length - 1; i >= 0; i--) { - path.append(hostTokens[i]); - if (i != 0) - path.append('/'); - } - return path.toString(); - } - - /** - * Creates a path from a UUID (e.g. 6ebda899-217d-4bf1-abe4-2839085c8f3c => - * 6ebda899-217d/4bf1/abe4/2839085c8f3c/). '/' at the end, not the beginning - */ - public static String uuidAsPath(String uuid) { - StringBuffer path = new StringBuffer(uuid.length()); - String[] tokens = uuid.split("-"); - for (int i = 0; i < tokens.length; i++) { - path.append(tokens[i]); - if (i != 0) - path.append('/'); - } - return path.toString(); - } - - /** - * The provided data as a path ('/' at the end, not the beginning) - * - * @param cal - * the date - * @param addHour - * whether to add hour as well - */ - public static String dateAsPath(Calendar cal, Boolean addHour) { - StringBuffer buf = new StringBuffer(14); - buf.append('Y'); - buf.append(cal.get(Calendar.YEAR)); - buf.append('/'); - - int month = cal.get(Calendar.MONTH) + 1; - buf.append('M'); - if (month < 10) - buf.append(0); - buf.append(month); - buf.append('/'); - - int day = cal.get(Calendar.DAY_OF_MONTH); - buf.append('D'); - if (day < 10) - buf.append(0); - buf.append(day); - buf.append('/'); - - if (addHour) { - int hour = cal.get(Calendar.HOUR_OF_DAY); - buf.append('H'); - if (hour < 10) - buf.append(0); - buf.append(hour); - buf.append('/'); - } - return buf.toString(); - - } - - /** Converts in one call a string into a gregorian calendar. */ - public static Calendar parseCalendar(DateFormat dateFormat, String value) { - try { - Date date = dateFormat.parse(value); - Calendar calendar = new GregorianCalendar(); - calendar.setTime(date); - return calendar; - } catch (ParseException e) { - throw new ArgeoJcrException("Cannot parse " + value - + " with date format " + dateFormat, e); - } - - } - - /** The last element of a path. */ - public static String lastPathElement(String path) { - if (path.charAt(path.length() - 1) == '/') - throw new ArgeoJcrException("Path " + path + " cannot end with '/'"); - int index = path.lastIndexOf('/'); - if (index < 0) - return path; - return path.substring(index + 1); - } - - /** - * Call {@link Node#getName()} without exceptions (useful in super - * constructors). - */ - public static String getNameQuietly(Node node) { - try { - return node.getName(); - } catch (RepositoryException e) { - throw new ArgeoJcrException("Cannot get name from " + node, e); - } - } - - /** - * Call {@link Node#getProperty(String)} without exceptions (useful in super - * constructors). - */ - public static String getStringPropertyQuietly(Node node, String propertyName) { - try { - return node.getProperty(propertyName).getString(); - } catch (RepositoryException e) { - throw new ArgeoJcrException("Cannot get name from " + node, e); - } - } - - /** - * Routine that get the child with this name, adding id it does not already - * exist - */ - public static Node getOrAdd(Node parent, String childName, - String childPrimaryNodeType) throws RepositoryException { - return parent.hasNode(childName) ? parent.getNode(childName) : parent - .addNode(childName, childPrimaryNodeType); - } - - /** - * Routine that get the child with this name, adding id it does not already - * exist - */ - public static Node getOrAdd(Node parent, String childName) - throws RepositoryException { - return parent.hasNode(childName) ? parent.getNode(childName) : parent - .addNode(childName); - } - - /** Convert a {@link NodeIterator} to a list of {@link Node} */ - public static List nodeIteratorToList(NodeIterator nodeIterator) { - List nodes = new ArrayList(); - while (nodeIterator.hasNext()) { - nodes.add(nodeIterator.nextNode()); - } - return nodes; - } - - /* - * PROPERTIES - */ - - /** - * Concisely get the string value of a property or null if this node doesn't - * have this property - */ - public static String get(Node node, String propertyName) { - try { - if (!node.hasProperty(propertyName)) - return null; - return node.getProperty(propertyName).getString(); - } catch (RepositoryException e) { - throw new ArgeoJcrException("Cannot get property " + propertyName - + " of " + node, e); - } - } - - /** Concisely get the boolean value of a property */ - public static Boolean check(Node node, String propertyName) { - try { - return node.getProperty(propertyName).getBoolean(); - } catch (RepositoryException e) { - throw new ArgeoJcrException("Cannot get property " + propertyName - + " of " + node, e); - } - } - - /** Concisely get the bytes array value of a property */ - public static byte[] getBytes(Node node, String propertyName) { - try { - return getBinaryAsBytes(node.getProperty(propertyName)); - } catch (RepositoryException e) { - throw new ArgeoJcrException("Cannot get property " + propertyName - + " of " + node, e); - } - } - - /** Creates the nodes making path, if they don't exist. */ - public static Node mkdirs(Session session, String path) { - return mkdirs(session, path, null, null, false); - } - - /** - * use {@link #mkdirs(Session, String, String, String, Boolean)} instead. - * - * @deprecated - */ - @Deprecated - public static Node mkdirs(Session session, String path, String type, - Boolean versioning) { - return mkdirs(session, path, type, type, false); - } - - /** - * @param type - * the type of the leaf node - */ - public static Node mkdirs(Session session, String path, String type) { - return mkdirs(session, path, type, null, false); - } - - /** - * Create sub nodes relative to a parent node - * - * @param nodeType - * the type of the leaf node - */ - public static Node mkdirs(Node parentNode, String relativePath, - String nodeType) { - return mkdirs(parentNode, relativePath, nodeType, null); - } - - /** - * Create sub nodes relative to a parent node - * - * @param nodeType - * the type of the leaf node - */ - public static Node mkdirs(Node parentNode, String relativePath, - String nodeType, String intermediaryNodeType) { - List tokens = tokenize(relativePath); - Node currParent = parentNode; - try { - for (int i = 0; i < tokens.size(); i++) { - String name = tokens.get(i); - if (currParent.hasNode(name)) { - currParent = currParent.getNode(name); - } else { - if (i != (tokens.size() - 1)) {// intermediary - currParent = currParent.addNode(name, - intermediaryNodeType); - } else {// leaf - currParent = currParent.addNode(name, nodeType); - } - } - } - return currParent; - } catch (RepositoryException e) { - throw new ArgeoJcrException("Cannot mkdirs relative path " - + relativePath + " from " + parentNode, e); - } - } - - /** - * Synchronized and save is performed, to avoid race conditions in - * initializers leading to duplicate nodes. - */ - public synchronized static Node mkdirsSafe(Session session, String path, - String type) { - try { - if (session.hasPendingChanges()) - throw new ArgeoJcrException( - "Session has pending changes, save them first."); - Node node = mkdirs(session, path, type); - session.save(); - return node; - } catch (RepositoryException e) { - discardQuietly(session); - throw new ArgeoJcrException("Cannot safely make directories", e); - } - } - - public synchronized static Node mkdirsSafe(Session session, String path) { - return mkdirsSafe(session, path, null); - } - - /** - * Creates the nodes making path, if they don't exist. This is up to the - * caller to save the session. Use with caution since it can create - * duplicate nodes if used concurrently. - */ - public static Node mkdirs(Session session, String path, String type, - String intermediaryNodeType, Boolean versioning) { - try { - if (path.equals('/')) - return session.getRootNode(); - - if (session.itemExists(path)) { - Node node = session.getNode(path); - // check type - if (type != null && !node.isNodeType(type) - && !node.getPath().equals("/")) - throw new ArgeoJcrException("Node " + node - + " exists but is of type " - + node.getPrimaryNodeType().getName() - + " not of type " + type); - // TODO: check versioning - return node; - } - - StringBuffer current = new StringBuffer("/"); - Node currentNode = session.getRootNode(); - Iterator it = tokenize(path).iterator(); - while (it.hasNext()) { - String part = it.next(); - current.append(part).append('/'); - if (!session.itemExists(current.toString())) { - if (!it.hasNext() && type != null) - currentNode = currentNode.addNode(part, type); - else if (it.hasNext() && intermediaryNodeType != null) - currentNode = currentNode.addNode(part, - intermediaryNodeType); - else - currentNode = currentNode.addNode(part); - if (versioning) - currentNode.addMixin(NodeType.MIX_VERSIONABLE); - if (log.isTraceEnabled()) - log.debug("Added folder " + part + " as " + current); - } else { - currentNode = (Node) session.getItem(current.toString()); - } - } - return currentNode; - } catch (RepositoryException e) { - discardQuietly(session); - throw new ArgeoJcrException("Cannot mkdirs " + path, e); - } finally { - } - } - - /** Convert a path to the list of its tokens */ - public static List tokenize(String path) { - List tokens = new ArrayList(); - boolean optimized = false; - if (!optimized) { - String[] rawTokens = path.split("/"); - for (String token : rawTokens) { - if (!token.equals("")) - tokens.add(token); - } - } else { - StringBuffer curr = new StringBuffer(); - char[] arr = path.toCharArray(); - chars: for (int i = 0; i < arr.length; i++) { - char c = arr[i]; - if (c == '/') { - if (i == 0 || (i == arr.length - 1)) - continue chars; - if (curr.length() > 0) { - tokens.add(curr.toString()); - curr = new StringBuffer(); - } - } else - curr.append(c); - } - if (curr.length() > 0) { - tokens.add(curr.toString()); - curr = new StringBuffer(); - } - } - return Collections.unmodifiableList(tokens); - } - - /** - * Safe and repository implementation independent registration of a - * namespace. - */ - public static void registerNamespaceSafely(Session session, String prefix, - String uri) { - try { - registerNamespaceSafely(session.getWorkspace() - .getNamespaceRegistry(), prefix, uri); - } catch (RepositoryException e) { - throw new ArgeoJcrException("Cannot find namespace registry", e); - } - } - - /** - * Safe and repository implementation independent registration of a - * namespace. - */ - public static void registerNamespaceSafely(NamespaceRegistry nr, - String prefix, String uri) { - try { - String[] prefixes = nr.getPrefixes(); - for (String pref : prefixes) - if (pref.equals(prefix)) { - String registeredUri = nr.getURI(pref); - if (!registeredUri.equals(uri)) - throw new ArgeoJcrException("Prefix " + pref - + " already registered for URI " - + registeredUri - + " which is different from provided URI " - + uri); - else - return;// skip - } - nr.registerNamespace(prefix, uri); - } catch (RepositoryException e) { - throw new ArgeoJcrException("Cannot register namespace " + uri - + " under prefix " + prefix, e); - } - } - - /** Recursively outputs the contents of the given node. */ - public static void debug(Node node) { - debug(node, log); - } - - /** Recursively outputs the contents of the given node. */ - public static void debug(Node node, Log log) { - try { - // First output the node path - log.debug(node.getPath()); - // Skip the virtual (and large!) jcr:system subtree - if (node.getName().equals("jcr:system")) { - return; - } - - // Then the children nodes (recursive) - NodeIterator it = node.getNodes(); - while (it.hasNext()) { - Node childNode = it.nextNode(); - debug(childNode, log); - } - - // Then output the properties - PropertyIterator properties = node.getProperties(); - // log.debug("Property are : "); - - properties: while (properties.hasNext()) { - Property property = properties.nextProperty(); - if (property.getType() == PropertyType.BINARY) - continue properties;// skip - if (property.getDefinition().isMultiple()) { - // A multi-valued property, print all values - Value[] values = property.getValues(); - for (int i = 0; i < values.length; i++) { - log.debug(property.getPath() + "=" - + values[i].getString()); - } - } else { - // A single-valued property - log.debug(property.getPath() + "=" + property.getString()); - } - } - } catch (Exception e) { - log.error("Could not debug " + node, e); - } - - } - - /** Logs the effective access control policies */ - public static void logEffectiveAccessPolicies(Node node) { - try { - logEffectiveAccessPolicies(node.getSession(), node.getPath()); - } catch (RepositoryException e) { - log.error("Cannot log effective access policies of " + node, e); - } - } - - /** Logs the effective access control policies */ - public static void logEffectiveAccessPolicies(Session session, String path) { - if (!log.isDebugEnabled()) - return; - - try { - AccessControlPolicy[] effectivePolicies = session - .getAccessControlManager().getEffectivePolicies(path); - if (effectivePolicies.length > 0) { - for (AccessControlPolicy policy : effectivePolicies) { - if (policy instanceof AccessControlList) { - AccessControlList acl = (AccessControlList) policy; - log.debug("Access control list for " + path + "\n" - + accessControlListSummary(acl)); - } - } - } else { - log.debug("No effective access control policy for " + path); - } - } catch (RepositoryException e) { - log.error("Cannot log effective access policies of " + path, e); - } - } - - /** Returns a human-readable summary of this access control list. */ - public static String accessControlListSummary(AccessControlList acl) { - StringBuffer buf = new StringBuffer(""); - try { - for (AccessControlEntry ace : acl.getAccessControlEntries()) { - buf.append('\t').append(ace.getPrincipal().getName()) - .append('\n'); - for (Privilege priv : ace.getPrivileges()) - buf.append("\t\t").append(priv.getName()).append('\n'); - } - return buf.toString(); - } catch (RepositoryException e) { - throw new ArgeoJcrException("Cannot write summary of " + acl, e); - } - } - - /** - * Copies recursively the content of a node to another one. Do NOT copy the - * property values of {@link NodeType#MIX_CREATED} and - * {@link NodeType#MIX_LAST_MODIFIED}, but update the - * {@link Property#JCR_LAST_MODIFIED} and - * {@link Property#JCR_LAST_MODIFIED_BY} properties if the target node has - * the {@link NodeType#MIX_LAST_MODIFIED} mixin. - */ - public static void copy(Node fromNode, Node toNode) { - try { - if (toNode.getDefinition().isProtected()) - return; - - // process properties - PropertyIterator pit = fromNode.getProperties(); - properties: while (pit.hasNext()) { - Property fromProperty = pit.nextProperty(); - String propertyName = fromProperty.getName(); - if (toNode.hasProperty(propertyName) - && toNode.getProperty(propertyName).getDefinition() - .isProtected()) - continue properties; - - if (fromProperty.getDefinition().isProtected()) - continue properties; - - if (propertyName.equals("jcr:created") - || propertyName.equals("jcr:createdBy") - || propertyName.equals("jcr:lastModified") - || propertyName.equals("jcr:lastModifiedBy")) - continue properties; - - if (fromProperty.isMultiple()) { - toNode.setProperty(propertyName, fromProperty.getValues()); - } else { - toNode.setProperty(propertyName, fromProperty.getValue()); - } - } - - // update jcr:lastModified and jcr:lastModifiedBy in toNode in case - // they existed, before adding the mixins - updateLastModified(toNode); - - // add mixins - for (NodeType mixinType : fromNode.getMixinNodeTypes()) { - toNode.addMixin(mixinType.getName()); - } - - // process children nodes - NodeIterator nit = fromNode.getNodes(); - while (nit.hasNext()) { - Node fromChild = nit.nextNode(); - Integer index = fromChild.getIndex(); - String nodeRelPath = fromChild.getName() + "[" + index + "]"; - Node toChild; - if (toNode.hasNode(nodeRelPath)) - toChild = toNode.getNode(nodeRelPath); - else - toChild = toNode.addNode(fromChild.getName(), fromChild - .getPrimaryNodeType().getName()); - copy(fromChild, toChild); - } - } catch (RepositoryException e) { - throw new ArgeoJcrException("Cannot copy " + fromNode + " to " - + toNode, e); - } - } - - /** - * Check whether all first-level properties (except jcr:* properties) are - * equal. Skip jcr:* properties - */ - public static Boolean allPropertiesEquals(Node reference, Node observed, - Boolean onlyCommonProperties) { - try { - PropertyIterator pit = reference.getProperties(); - props: while (pit.hasNext()) { - Property propReference = pit.nextProperty(); - String propName = propReference.getName(); - if (propName.startsWith("jcr:")) - continue props; - - if (!observed.hasProperty(propName)) - if (onlyCommonProperties) - continue props; - else - return false; - // TODO: deal with multiple property values? - if (!observed.getProperty(propName).getValue() - .equals(propReference.getValue())) - return false; - } - return true; - } catch (RepositoryException e) { - throw new ArgeoJcrException("Cannot check all properties equals of " - + reference + " and " + observed, e); - } - } - - public static Map diffProperties(Node reference, - Node observed) { - Map diffs = new TreeMap(); - diffPropertiesLevel(diffs, null, reference, observed); - return diffs; - } - - /** - * Compare the properties of two nodes. Recursivity to child nodes is not - * yet supported. Skip jcr:* properties. - */ - static void diffPropertiesLevel(Map diffs, - String baseRelPath, Node reference, Node observed) { - try { - // check removed and modified - PropertyIterator pit = reference.getProperties(); - props: while (pit.hasNext()) { - Property p = pit.nextProperty(); - String name = p.getName(); - if (name.startsWith("jcr:")) - continue props; - - if (!observed.hasProperty(name)) { - String relPath = propertyRelPath(baseRelPath, name); - PropertyDiff pDiff = new PropertyDiff(PropertyDiff.REMOVED, - relPath, p.getValue(), null); - diffs.put(relPath, pDiff); - } else { - if (p.isMultiple()) { - // FIXME implement multiple - } else { - Value referenceValue = p.getValue(); - Value newValue = observed.getProperty(name).getValue(); - if (!referenceValue.equals(newValue)) { - String relPath = propertyRelPath(baseRelPath, name); - PropertyDiff pDiff = new PropertyDiff( - PropertyDiff.MODIFIED, relPath, - referenceValue, newValue); - diffs.put(relPath, pDiff); - } - } - } - } - // check added - pit = observed.getProperties(); - props: while (pit.hasNext()) { - Property p = pit.nextProperty(); - String name = p.getName(); - if (name.startsWith("jcr:")) - continue props; - if (!reference.hasProperty(name)) { - if (p.isMultiple()) { - // FIXME implement multiple - } else { - String relPath = propertyRelPath(baseRelPath, name); - PropertyDiff pDiff = new PropertyDiff( - PropertyDiff.ADDED, relPath, null, p.getValue()); - diffs.put(relPath, pDiff); - } - } - } - } catch (RepositoryException e) { - throw new ArgeoJcrException("Cannot diff " + reference + " and " - + observed, e); - } - } - - /** - * Compare only a restricted list of properties of two nodes. No - * recursivity. - * - */ - public static Map diffProperties(Node reference, - Node observed, List properties) { - Map diffs = new TreeMap(); - try { - Iterator pit = properties.iterator(); - - props: while (pit.hasNext()) { - String name = pit.next(); - if (!reference.hasProperty(name)) { - if (!observed.hasProperty(name)) - continue props; - Value val = observed.getProperty(name).getValue(); - try { - // empty String but not null - if ("".equals(val.getString())) - continue props; - } catch (Exception e) { - // not parseable as String, silent - } - PropertyDiff pDiff = new PropertyDiff(PropertyDiff.ADDED, - name, null, val); - diffs.put(name, pDiff); - } else if (!observed.hasProperty(name)) { - PropertyDiff pDiff = new PropertyDiff(PropertyDiff.REMOVED, - name, reference.getProperty(name).getValue(), null); - diffs.put(name, pDiff); - } else { - Value referenceValue = reference.getProperty(name) - .getValue(); - Value newValue = observed.getProperty(name).getValue(); - if (!referenceValue.equals(newValue)) { - PropertyDiff pDiff = new PropertyDiff( - PropertyDiff.MODIFIED, name, referenceValue, - newValue); - diffs.put(name, pDiff); - } - } - } - } catch (RepositoryException e) { - throw new ArgeoJcrException("Cannot diff " + reference + " and " - + observed, e); - } - return diffs; - } - - /** Builds a property relPath to be used in the diff. */ - private static String propertyRelPath(String baseRelPath, - String propertyName) { - if (baseRelPath == null) - return propertyName; - else - return baseRelPath + '/' + propertyName; - } - - /** - * Normalizes a name so that it can be stored in contexts not supporting - * names with ':' (typically databases). Replaces ':' by '_'. - */ - public static String normalize(String name) { - return name.replace(':', '_'); - } - - /** - * Replaces characters which are invalid in a JCR name by '_'. Currently not - * exhaustive. - * - * @see JcrUtils#INVALID_NAME_CHARACTERS - */ - public static String replaceInvalidChars(String name) { - return replaceInvalidChars(name, '_'); - } - - /** - * Replaces characters which are invalid in a JCR name. Currently not - * exhaustive. - * - * @see JcrUtils#INVALID_NAME_CHARACTERS - */ - public static String replaceInvalidChars(String name, char replacement) { - boolean modified = false; - char[] arr = name.toCharArray(); - for (int i = 0; i < arr.length; i++) { - char c = arr[i]; - invalid: for (char invalid : INVALID_NAME_CHARACTERS) { - if (c == invalid) { - arr[i] = replacement; - modified = true; - break invalid; - } - } - } - if (modified) - return new String(arr); - else - // do not create new object if unnecessary - return name; - } - - /** - * Removes forbidden characters from a path, replacing them with '_' - * - * @deprecated use {@link #replaceInvalidChars(String)} instead - */ - public static String removeForbiddenCharacters(String str) { - return str.replace('[', '_').replace(']', '_').replace('/', '_') - .replace('*', '_'); - - } - - /** Cleanly disposes a {@link Binary} even if it is null. */ - public static void closeQuietly(Binary binary) { - if (binary == null) - return; - binary.dispose(); - } - - /** Retrieve a {@link Binary} as a byte array */ - public static byte[] getBinaryAsBytes(Property property) { - ByteArrayOutputStream out = new ByteArrayOutputStream(); - InputStream in = null; - Binary binary = null; - try { - binary = property.getBinary(); - in = binary.getStream(); - IOUtils.copy(in, out); - return out.toByteArray(); - } catch (Exception e) { - throw new ArgeoJcrException("Cannot read binary " + property - + " as bytes", e); - } finally { - IOUtils.closeQuietly(out); - IOUtils.closeQuietly(in); - closeQuietly(binary); - } - } - - /** Writes a {@link Binary} from a byte array */ - public static void setBinaryAsBytes(Node node, String property, byte[] bytes) { - InputStream in = null; - Binary binary = null; - try { - in = new ByteArrayInputStream(bytes); - binary = node.getSession().getValueFactory().createBinary(in); - node.setProperty(property, binary); - } catch (Exception e) { - throw new ArgeoJcrException("Cannot read binary " + property - + " as bytes", e); - } finally { - IOUtils.closeQuietly(in); - closeQuietly(binary); - } - } - - /** - * Creates depth from a string (typically a username) by adding levels based - * on its first characters: "aBcD",2 => a/aB - */ - public static String firstCharsToPath(String str, Integer nbrOfChars) { - if (str.length() < nbrOfChars) - throw new ArgeoJcrException("String " + str - + " length must be greater or equal than " + nbrOfChars); - StringBuffer path = new StringBuffer(""); - StringBuffer curr = new StringBuffer(""); - for (int i = 0; i < nbrOfChars; i++) { - curr.append(str.charAt(i)); - path.append(curr); - if (i < nbrOfChars - 1) - path.append('/'); - } - return path.toString(); - } - - /** - * Discards the current changes in the session attached to this node. To be - * used typically in a catch block. - * - * @see #discardQuietly(Session) - */ - public static void discardUnderlyingSessionQuietly(Node node) { - try { - discardQuietly(node.getSession()); - } catch (RepositoryException e) { - log.warn("Cannot quietly discard session of node " + node + ": " - + e.getMessage()); - } - } - - /** - * Discards the current changes in a session by calling - * {@link Session#refresh(boolean)} with false, only logging - * potential errors when doing so. To be used typically in a catch block. - */ - public static void discardQuietly(Session session) { - try { - if (session != null) - session.refresh(false); - } catch (RepositoryException e) { - log.warn("Cannot quietly discard session " + session + ": " - + e.getMessage()); - } - } - - /** - * Login to a workspace with implicit credentials, creates the workspace - * with these credentials if it does not already exist. - */ - public static Session loginOrCreateWorkspace(Repository repository, - String workspaceName) throws RepositoryException { - Session workspaceSession = null; - Session defaultSession = null; - try { - try { - workspaceSession = repository.login(workspaceName); - } catch (NoSuchWorkspaceException e) { - // try to create workspace - defaultSession = repository.login(); - defaultSession.getWorkspace().createWorkspace(workspaceName); - workspaceSession = repository.login(workspaceName); - } - return workspaceSession; - } finally { - logoutQuietly(defaultSession); - } - } - - /** Logs out the session, not throwing any exception, even if it is null. */ - public static void logoutQuietly(Session session) { - try { - if (session != null) - if (session.isLive()) - session.logout(); - } catch (Exception e) { - // silent - } - } - - /** - * Convenient method to add a listener. uuids passed as null, deep=true, - * local=true, only one node type - */ - public static void addListener(Session session, EventListener listener, - int eventTypes, String basePath, String nodeType) { - try { - session.getWorkspace() - .getObservationManager() - .addEventListener( - listener, - eventTypes, - basePath, - true, - null, - nodeType == null ? null : new String[] { nodeType }, - true); - } catch (RepositoryException e) { - throw new ArgeoJcrException("Cannot add JCR listener " + listener - + " to session " + session, e); - } - } - - /** Removes a listener without throwing exception */ - public static void removeListenerQuietly(Session session, - EventListener listener) { - if (session == null || !session.isLive()) - return; - try { - session.getWorkspace().getObservationManager() - .removeEventListener(listener); - } catch (RepositoryException e) { - // silent - } - } - - /** - * Quietly unregisters an {@link EventListener} from the udnerlying - * workspace of this node. - */ - public static void unregisterQuietly(Node node, EventListener eventListener) { - try { - unregisterQuietly(node.getSession().getWorkspace(), eventListener); - } catch (RepositoryException e) { - // silent - if (log.isTraceEnabled()) - log.trace("Could not unregister event listener " - + eventListener); - } - } - - /** Quietly unregisters an {@link EventListener} from this workspace */ - public static void unregisterQuietly(Workspace workspace, - EventListener eventListener) { - if (eventListener == null) - return; - try { - workspace.getObservationManager() - .removeEventListener(eventListener); - } catch (RepositoryException e) { - // silent - if (log.isTraceEnabled()) - log.trace("Could not unregister event listener " - + eventListener); - } - } - - /** - * If this node is has the {@link NodeType#MIX_LAST_MODIFIED} mixin, it - * updates the {@link Property#JCR_LAST_MODIFIED} property with the current - * time and the {@link Property#JCR_LAST_MODIFIED_BY} property with the - * underlying session user id. In Jackrabbit 2.x, these properties - * are not automatically updated, hence the need for manual update. The - * session is not saved. - */ - public static void updateLastModified(Node node) { - try { - if (!node.isNodeType(NodeType.MIX_LAST_MODIFIED)) - node.addMixin(NodeType.MIX_LAST_MODIFIED); - node.setProperty(Property.JCR_LAST_MODIFIED, - new GregorianCalendar()); - node.setProperty(Property.JCR_LAST_MODIFIED_BY, node.getSession() - .getUserID()); - } catch (RepositoryException e) { - throw new ArgeoJcrException("Cannot update last modified on " + node, - e); - } - } - - /** - * Update lastModified recursively until this parent. - * - * @param node - * the node - * @param untilPath - * the base path, null is equivalent to "/" - */ - public static void updateLastModifiedAndParents(Node node, String untilPath) { - try { - if (untilPath != null && !node.getPath().startsWith(untilPath)) - throw new ArgeoJcrException(node + " is not under " + untilPath); - updateLastModified(node); - if (untilPath == null) { - if (!node.getPath().equals("/")) - updateLastModifiedAndParents(node.getParent(), untilPath); - } else { - if (!node.getPath().equals(untilPath)) - updateLastModifiedAndParents(node.getParent(), untilPath); - } - } catch (RepositoryException e) { - throw new ArgeoJcrException("Cannot update lastModified from " + node - + " until " + untilPath, e); - } - } - - /** - * Returns a String representing the short version (see Node type - * Notation attributes grammar) of the main business attributes of this - * property definition - * - * @param prop - */ - public static String getPropertyDefinitionAsString(Property prop) { - StringBuffer sbuf = new StringBuffer(); - try { - if (prop.getDefinition().isAutoCreated()) - sbuf.append("a"); - if (prop.getDefinition().isMandatory()) - sbuf.append("m"); - if (prop.getDefinition().isProtected()) - sbuf.append("p"); - if (prop.getDefinition().isMultiple()) - sbuf.append("*"); - } catch (RepositoryException re) { - throw new ArgeoJcrException( - "unexpected error while getting property definition as String", - re); - } - return sbuf.toString(); - } - - /** - * Estimate the sub tree size from current node. Computation is based on the - * Jcr {@link Property.getLength()} method. Note : it is not the exact size - * used on the disk by the current part of the JCR Tree. - */ - - public static long getNodeApproxSize(Node node) { - long curNodeSize = 0; - try { - PropertyIterator pi = node.getProperties(); - while (pi.hasNext()) { - Property prop = pi.nextProperty(); - if (prop.isMultiple()) { - int nb = prop.getLengths().length; - for (int i = 0; i < nb; i++) { - curNodeSize += (prop.getLengths()[i] > 0 ? prop - .getLengths()[i] : 0); - } - } else - curNodeSize += (prop.getLength() > 0 ? prop.getLength() : 0); - } - - NodeIterator ni = node.getNodes(); - while (ni.hasNext()) - curNodeSize += getNodeApproxSize(ni.nextNode()); - return curNodeSize; - } catch (RepositoryException re) { - throw new ArgeoJcrException( - "Unexpected error while recursively determining node size.", - re); - } - } - - /* - * SECURITY - */ - - /** - * Convenience method for adding a single privilege to a principal (user or - * role), typically jcr:all - */ - public synchronized static void addPrivilege(Session session, String path, - String principal, String privilege) throws RepositoryException { - List privileges = new ArrayList(); - privileges.add(session.getAccessControlManager().privilegeFromName( - privilege)); - addPrivileges(session, path, new SimplePrincipal(principal), privileges); - } - - /** - * Add privileges on a path to a {@link Principal}. The path must already - * exist. Session is saved. Synchronized to prevent concurrent modifications - * of the same node. - */ - public synchronized static Boolean addPrivileges(Session session, - String path, Principal principal, List privs) - throws RepositoryException { - // make sure the session is in line with the persisted state - session.refresh(false); - AccessControlManager acm = session.getAccessControlManager(); - AccessControlList acl = getAccessControlList(acm, path); - - accessControlEntries: for (AccessControlEntry ace : acl - .getAccessControlEntries()) { - Principal currentPrincipal = ace.getPrincipal(); - if (currentPrincipal.getName().equals(principal.getName())) { - Privilege[] currentPrivileges = ace.getPrivileges(); - if (currentPrivileges.length != privs.size()) - break accessControlEntries; - for (int i = 0; i < currentPrivileges.length; i++) { - Privilege currP = currentPrivileges[i]; - Privilege p = privs.get(i); - if (!currP.getName().equals(p.getName())) { - break accessControlEntries; - } - } - return false; - } - } - - Privilege[] privileges = privs.toArray(new Privilege[privs.size()]); - acl.addAccessControlEntry(principal, privileges); - acm.setPolicy(path, acl); - if (log.isDebugEnabled()) { - StringBuffer privBuf = new StringBuffer(); - for (Privilege priv : privs) - privBuf.append(priv.getName()); - log.debug("Added privileges " + privBuf + " to " - + principal.getName() + " on " + path + " in '" - + session.getWorkspace().getName() + "'"); - } - session.refresh(true); - session.save(); - return true; - } - - /** Gets access control list for this path, throws exception if not found */ - public synchronized static AccessControlList getAccessControlList( - AccessControlManager acm, String path) throws RepositoryException { - // search for an access control list - AccessControlList acl = null; - AccessControlPolicyIterator policyIterator = acm - .getApplicablePolicies(path); - if (policyIterator.hasNext()) { - while (policyIterator.hasNext()) { - AccessControlPolicy acp = policyIterator - .nextAccessControlPolicy(); - if (acp instanceof AccessControlList) - acl = ((AccessControlList) acp); - } - } else { - AccessControlPolicy[] existingPolicies = acm.getPolicies(path); - for (AccessControlPolicy acp : existingPolicies) { - if (acp instanceof AccessControlList) - acl = ((AccessControlList) acp); - } - } - if (acl != null) - return acl; - else - throw new ArgeoJcrException("ACL not found at " + path); - } - - /** Clear authorizations for a user at this path */ - public synchronized static void clearAccessControList(Session session, - String path, String username) throws RepositoryException { - AccessControlManager acm = session.getAccessControlManager(); - AccessControlList acl = getAccessControlList(acm, path); - for (AccessControlEntry ace : acl.getAccessControlEntries()) { - if (ace.getPrincipal().getName().equals(username)) { - acl.removeAccessControlEntry(ace); - } - } - // the new access control list must be applied otherwise this call: - // acl.removeAccessControlEntry(ace); has no effect - acm.setPolicy(path, acl); - } - - /* - * FILES UTILITIES - */ - /** - * Creates the nodes making the path as {@link NodeType#NT_FOLDER} - */ - public static Node mkfolders(Session session, String path) { - return mkdirs(session, path, NodeType.NT_FOLDER, NodeType.NT_FOLDER, - false); - } - - /** - * Copy only nt:folder and nt:file, without their additional types and - * properties. - * - * @param recursive - * if true copies folders as well, otherwise only first level - * files - * @return how many files were copied - */ - @SuppressWarnings("deprecation") - public static Long copyFiles(Node fromNode, Node toNode, Boolean recursive, - ArgeoMonitor monitor) { - long count = 0l; - - Binary binary = null; - InputStream in = null; - try { - NodeIterator fromChildren = fromNode.getNodes(); - while (fromChildren.hasNext()) { - if (monitor != null && monitor.isCanceled()) - throw new ArgeoJcrException( - "Copy cancelled before it was completed"); - - Node fromChild = fromChildren.nextNode(); - String fileName = fromChild.getName(); - if (fromChild.isNodeType(NodeType.NT_FILE)) { - if (monitor != null) - monitor.subTask("Copy " + fileName); - binary = fromChild.getNode(Node.JCR_CONTENT) - .getProperty(Property.JCR_DATA).getBinary(); - in = binary.getStream(); - copyStreamAsFile(toNode, fileName, in); - IOUtils.closeQuietly(in); - closeQuietly(binary); - - // save session - toNode.getSession().save(); - count++; - - if (log.isDebugEnabled()) - log.debug("Copied file " + fromChild.getPath()); - if (monitor != null) - monitor.worked(1); - } else if (fromChild.isNodeType(NodeType.NT_FOLDER) - && recursive) { - Node toChildFolder; - if (toNode.hasNode(fileName)) { - toChildFolder = toNode.getNode(fileName); - if (!toChildFolder.isNodeType(NodeType.NT_FOLDER)) - throw new ArgeoJcrException(toChildFolder - + " is not of type nt:folder"); - } else { - toChildFolder = toNode.addNode(fileName, - NodeType.NT_FOLDER); - - // save session - toNode.getSession().save(); - } - count = count - + copyFiles(fromChild, toChildFolder, recursive, - monitor); - } - } - return count; - } catch (RepositoryException e) { - throw new ArgeoJcrException("Cannot copy files between " + fromNode - + " and " + toNode); - } finally { - // in case there was an exception - IOUtils.closeQuietly(in); - closeQuietly(binary); - } - } - - /** - * Iteratively count all file nodes in subtree, inefficient but can be - * useful when query are poorly supported, such as in remoting. - */ - public static Long countFiles(Node node) { - Long localCount = 0l; - try { - for (NodeIterator nit = node.getNodes(); nit.hasNext();) { - Node child = nit.nextNode(); - if (child.isNodeType(NodeType.NT_FOLDER)) - localCount = localCount + countFiles(child); - else if (child.isNodeType(NodeType.NT_FILE)) - localCount = localCount + 1; - } - } catch (RepositoryException e) { - throw new ArgeoJcrException("Cannot count all children of " + node); - } - return localCount; - } - - /** - * Copy a file as an nt:file, assuming an nt:folder hierarchy. The session - * is NOT saved. - * - * @return the created file node - */ - public static Node copyFile(Node folderNode, File file) { - InputStream in = null; - try { - in = new FileInputStream(file); - return copyStreamAsFile(folderNode, file.getName(), in); - } catch (IOException e) { - throw new ArgeoJcrException("Cannot copy file " + file + " under " - + folderNode, e); - } finally { - IOUtils.closeQuietly(in); - } - } - - /** Copy bytes as an nt:file */ - public static Node copyBytesAsFile(Node folderNode, String fileName, - byte[] bytes) { - InputStream in = null; - try { - in = new ByteArrayInputStream(bytes); - return copyStreamAsFile(folderNode, fileName, in); - } catch (Exception e) { - throw new ArgeoJcrException("Cannot copy file " + fileName + " under " - + folderNode, e); - } finally { - IOUtils.closeQuietly(in); - } - } - - /** - * Copy a stream as an nt:file, assuming an nt:folder hierarchy. The session - * is NOT saved. - * - * @return the created file node - */ - public static Node copyStreamAsFile(Node folderNode, String fileName, - InputStream in) { - Binary binary = null; - try { - Node fileNode; - Node contentNode; - if (folderNode.hasNode(fileName)) { - fileNode = folderNode.getNode(fileName); - if (!fileNode.isNodeType(NodeType.NT_FILE)) - throw new ArgeoJcrException(fileNode - + " is not of type nt:file"); - // we assume that the content node is already there - contentNode = fileNode.getNode(Node.JCR_CONTENT); - } else { - fileNode = folderNode.addNode(fileName, NodeType.NT_FILE); - contentNode = fileNode.addNode(Node.JCR_CONTENT, - NodeType.NT_RESOURCE); - } - binary = contentNode.getSession().getValueFactory() - .createBinary(in); - contentNode.setProperty(Property.JCR_DATA, binary); - return fileNode; - } catch (Exception e) { - throw new ArgeoJcrException("Cannot create file node " + fileName - + " under " + folderNode, e); - } finally { - closeQuietly(binary); - } - } - - /** Computes the checksum of an nt:file */ - public static String checksumFile(Node fileNode, String algorithm) { - Binary data = null; - InputStream in = null; - try { - data = fileNode.getNode(Node.JCR_CONTENT) - .getProperty(Property.JCR_DATA).getBinary(); - in = data.getStream(); - return DigestUtils.digest(algorithm, in); - } catch (RepositoryException e) { - throw new ArgeoJcrException("Cannot checksum file " + fileNode, e); - } finally { - IOUtils.closeQuietly(in); - closeQuietly(data); - } - } - -} diff --git a/org.argeo.server.jcr/src/org/argeo/jcr/PropertyDiff.java b/org.argeo.server.jcr/src/org/argeo/jcr/PropertyDiff.java deleted file mode 100644 index a0ff4712a..000000000 --- a/org.argeo.server.jcr/src/org/argeo/jcr/PropertyDiff.java +++ /dev/null @@ -1,76 +0,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. - */ -package org.argeo.jcr; - -import javax.jcr.Value; - -/** The result of the comparison of two JCR properties. */ -public class PropertyDiff { - public final static Integer MODIFIED = 0; - public final static Integer ADDED = 1; - public final static Integer REMOVED = 2; - - private final Integer type; - private final String relPath; - private final Value referenceValue; - private final Value newValue; - - public PropertyDiff(Integer type, String relPath, Value referenceValue, - Value newValue) { - super(); - - if (type == MODIFIED) { - if (referenceValue == null || newValue == null) - throw new ArgeoJcrException( - "Reference and new values must be specified."); - } else if (type == ADDED) { - if (referenceValue != null || newValue == null) - throw new ArgeoJcrException( - "New value and only it must be specified."); - } else if (type == REMOVED) { - if (referenceValue == null || newValue != null) - throw new ArgeoJcrException( - "Reference value and only it must be specified."); - } else { - throw new ArgeoJcrException("Unkown diff type " + type); - } - - if (relPath == null) - throw new ArgeoJcrException("Relative path must be specified"); - - this.type = type; - this.relPath = relPath; - this.referenceValue = referenceValue; - this.newValue = newValue; - } - - public Integer getType() { - return type; - } - - public String getRelPath() { - return relPath; - } - - public Value getReferenceValue() { - return referenceValue; - } - - public Value getNewValue() { - return newValue; - } - -} diff --git a/org.argeo.server.jcr/src/org/argeo/jcr/RepositoryRegister.java b/org.argeo.server.jcr/src/org/argeo/jcr/RepositoryRegister.java deleted file mode 100644 index 2e3d4550d..000000000 --- a/org.argeo.server.jcr/src/org/argeo/jcr/RepositoryRegister.java +++ /dev/null @@ -1,31 +0,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. - */ -package org.argeo.jcr; - -import java.util.Map; - -import javax.jcr.Repository; -import javax.jcr.RepositoryFactory; - -/** Allows to register repositories by name. */ -public interface RepositoryRegister extends RepositoryFactory { - /** - * The registered {@link Repository} as a read-only map. Note that this - * method should be called for each access in order to be sure to be up to - * date in case repositories have registered/unregistered - */ - public Map getRepositories(); -} diff --git a/org.argeo.server.jcr/src/org/argeo/jcr/ThreadBoundJcrSessionFactory.java b/org.argeo.server.jcr/src/org/argeo/jcr/ThreadBoundJcrSessionFactory.java deleted file mode 100644 index 281bd015c..000000000 --- a/org.argeo.server.jcr/src/org/argeo/jcr/ThreadBoundJcrSessionFactory.java +++ /dev/null @@ -1,295 +0,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. - */ -package org.argeo.jcr; - -import java.lang.reflect.InvocationHandler; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.lang.reflect.Proxy; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.Iterator; -import java.util.List; -import java.util.Map; - -import javax.jcr.LoginException; -import javax.jcr.Repository; -import javax.jcr.RepositoryException; -import javax.jcr.Session; -import javax.jcr.SimpleCredentials; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - -/** Proxy JCR sessions and attach them to calling threads. */ -@Deprecated -public abstract class ThreadBoundJcrSessionFactory { - private final static Log log = LogFactory.getLog(ThreadBoundJcrSessionFactory.class); - - private Repository repository; - /** can be injected as list, only used if repository is null */ - private List repositories; - - private ThreadLocal session = new ThreadLocal(); - private final Session proxiedSession; - /** If workspace is null, default will be used. */ - private String workspace = null; - - private String defaultUsername = "demo"; - private String defaultPassword = "demo"; - private Boolean forceDefaultCredentials = false; - - private boolean active = true; - - // monitoring - private final List threads = Collections.synchronizedList(new ArrayList()); - private final Map activeSessions = Collections.synchronizedMap(new HashMap()); - private MonitoringThread monitoringThread; - - public ThreadBoundJcrSessionFactory() { - Class[] interfaces = { Session.class }; - proxiedSession = (Session) Proxy.newProxyInstance(ThreadBoundJcrSessionFactory.class.getClassLoader(), - interfaces, new JcrSessionInvocationHandler()); - } - - /** Logs in to the repository using various strategies. */ - protected synchronized Session login() { - if (!isActive()) - throw new ArgeoJcrException("Thread bound session factory inactive"); - - // discard session previously attached to this thread - Thread thread = Thread.currentThread(); - if (activeSessions.containsKey(thread.getId())) { - Session oldSession = activeSessions.remove(thread.getId()); - oldSession.logout(); - session.remove(); - } - - Session newSession = null; - // first try to login without credentials, assuming the underlying login - // module will have dealt with authentication (typically using Spring - // Security) - if (!forceDefaultCredentials) - try { - newSession = repository().login(workspace); - } catch (LoginException e1) { - log.warn("Cannot login without credentials: " + e1.getMessage()); - // invalid credentials, go to the next step - } catch (RepositoryException e1) { - // other kind of exception, fail - throw new ArgeoJcrException("Cannot log in to repository", e1); - } - - // log using default username / password (useful for testing purposes) - if (newSession == null) - try { - SimpleCredentials sc = new SimpleCredentials(defaultUsername, defaultPassword.toCharArray()); - newSession = repository().login(sc, workspace); - } catch (RepositoryException e) { - throw new ArgeoJcrException("Cannot log in to repository", e); - } - - session.set(newSession); - // Log and monitor new session - if (log.isTraceEnabled()) - log.trace("Logged in to JCR session " + newSession + "; userId=" + newSession.getUserID()); - - // monitoring - activeSessions.put(thread.getId(), newSession); - threads.add(thread); - return newSession; - } - - public Object getObject() { - return proxiedSession; - } - - public void init() throws Exception { - // log.error("SHOULD NOT BE USED ANYMORE"); - monitoringThread = new MonitoringThread(); - monitoringThread.start(); - } - - public void dispose() throws Exception { - // if (activeSessions.size() == 0) - // return; - - if (log.isTraceEnabled()) - log.trace("Cleaning up " + activeSessions.size() + " active JCR sessions..."); - - deactivate(); - for (Session sess : activeSessions.values()) { - JcrUtils.logoutQuietly(sess); - } - activeSessions.clear(); - } - - protected Boolean isActive() { - return active; - } - - protected synchronized void deactivate() { - active = false; - notifyAll(); - } - - protected synchronized void removeSession(Thread thread) { - if (!isActive()) - return; - activeSessions.remove(thread.getId()); - threads.remove(thread); - } - - protected synchronized void cleanDeadThreads() { - if (!isActive()) - return; - Iterator it = threads.iterator(); - while (it.hasNext()) { - Thread thread = it.next(); - if (!thread.isAlive() && isActive()) { - if (activeSessions.containsKey(thread.getId())) { - Session session = activeSessions.get(thread.getId()); - activeSessions.remove(thread.getId()); - session.logout(); - if (log.isTraceEnabled()) - log.trace("Cleaned up JCR session (userID=" + session.getUserID() + ") from dead thread " - + thread.getId()); - } - it.remove(); - } - } - try { - wait(1000); - } catch (InterruptedException e) { - // silent - } - } - - public Class getObjectType() { - return Session.class; - } - - public boolean isSingleton() { - return true; - } - - /** - * Called before a method is actually called, allowing to check the session - * or re-login it (e.g. if authentication has changed). The default - * implementation returns the session. - */ - protected Session preCall(Session session) { - return session; - } - - protected Repository repository() { - if (repository != null) - return repository; - if (repositories != null) { - // hardened for OSGi dynamic services - Iterator it = repositories.iterator(); - if (it.hasNext()) - return it.next(); - } - throw new ArgeoJcrException("No repository injected"); - } - - // /** Useful for declarative registration of OSGi services (blueprint) */ - // public void register(Repository repository, Map params) { - // this.repository = repository; - // } - // - // /** Useful for declarative registration of OSGi services (blueprint) */ - // public void unregister(Repository repository, Map params) { - // this.repository = null; - // } - - public void setRepository(Repository repository) { - this.repository = repository; - } - - public void setRepositories(List repositories) { - this.repositories = repositories; - } - - public void setDefaultUsername(String defaultUsername) { - this.defaultUsername = defaultUsername; - } - - public void setDefaultPassword(String defaultPassword) { - this.defaultPassword = defaultPassword; - } - - public void setForceDefaultCredentials(Boolean forceDefaultCredentials) { - this.forceDefaultCredentials = forceDefaultCredentials; - } - - public void setWorkspace(String workspace) { - this.workspace = workspace; - } - - protected class JcrSessionInvocationHandler implements InvocationHandler { - - public Object invoke(Object proxy, Method method, Object[] args) throws Throwable, RepositoryException { - Session threadSession = session.get(); - if (threadSession == null) { - if ("logout".equals(method.getName()))// no need to login - return Void.TYPE; - else if ("toString".equals(method.getName()))// maybe logging - return "Uninitialized Argeo thread bound JCR session"; - threadSession = login(); - } - - preCall(threadSession); - Object ret; - try { - ret = method.invoke(threadSession, args); - } catch (InvocationTargetException e) { - Throwable cause = e.getCause(); - if (cause instanceof RepositoryException) - throw (RepositoryException) cause; - else - throw cause; - } - if ("logout".equals(method.getName())) { - session.remove(); - Thread thread = Thread.currentThread(); - removeSession(thread); - if (log.isTraceEnabled()) - log.trace("Logged out JCR session (userId=" + threadSession.getUserID() + ") on thread " - + thread.getId()); - } - return ret; - } - } - - /** Monitors registered thread in order to clean up dead ones. */ - private class MonitoringThread extends Thread { - - public MonitoringThread() { - super("ThreadBound JCR Session Monitor"); - } - - @Override - public void run() { - while (isActive()) { - cleanDeadThreads(); - } - } - - } -} diff --git a/org.argeo.server.jcr/src/org/argeo/jcr/UserJcrUtils.java b/org.argeo.server.jcr/src/org/argeo/jcr/UserJcrUtils.java deleted file mode 100644 index 95269475e..000000000 --- a/org.argeo.server.jcr/src/org/argeo/jcr/UserJcrUtils.java +++ /dev/null @@ -1,98 +0,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. - */ -package org.argeo.jcr; - -import javax.jcr.Node; -import javax.jcr.RepositoryException; -import javax.jcr.Session; -import javax.jcr.query.Query; -import javax.jcr.query.qom.Constraint; -import javax.jcr.query.qom.DynamicOperand; -import javax.jcr.query.qom.QueryObjectModelFactory; -import javax.jcr.query.qom.Selector; -import javax.jcr.query.qom.StaticOperand; - -/** - * Utilities related to the user home and properties based on Argeo JCR model. - * Do not use anymore. Does not fit with current security model - */ -@Deprecated -public class UserJcrUtils { - /** The home base path. Not yet configurable */ - public final static String DEFAULT_HOME_BASE_PATH = "/home"; - - /** - * Returns the home node of the user or null if none was found. - * - * @param session - * the session to use in order to perform the search, this can be - * a session with a different user ID than the one searched, - * typically when a system or admin session is used. - * @param username - * the username of the user - */ - public static Node getUserHome(Session session, String username) { - try { - // String homePath = UserJcrUtils.getUserHomePath(username); - // return session.itemExists(homePath) ? session.getNode(homePath) - // : null; - // kept for example of QOM queries - QueryObjectModelFactory qomf = session.getWorkspace() - .getQueryManager().getQOMFactory(); - Selector userHomeSel = qomf.selector(ArgeoTypes.ARGEO_USER_HOME, - "userHome"); - DynamicOperand userIdDop = qomf.propertyValue( - userHomeSel.getSelectorName(), ArgeoNames.ARGEO_USER_ID); - StaticOperand userIdSop = qomf.literal(session.getValueFactory() - .createValue(username)); - Constraint constraint = qomf.comparison(userIdDop, - QueryObjectModelFactory.JCR_OPERATOR_EQUAL_TO, userIdSop); - Query query = qomf.createQuery(userHomeSel, constraint, null, null); - return JcrUtils.querySingleNode(query); - } catch (RepositoryException e) { - throw new ArgeoJcrException("Cannot find home for user " + username, e); - } - } - - public static Node getUserProfile(Session session, String username) { - try { - QueryObjectModelFactory qomf = session.getWorkspace() - .getQueryManager().getQOMFactory(); - Selector userHomeSel = qomf.selector(ArgeoTypes.ARGEO_USER_PROFILE, - "userProfile"); - DynamicOperand userIdDop = qomf.propertyValue( - userHomeSel.getSelectorName(), ArgeoNames.ARGEO_USER_ID); - StaticOperand userIdSop = qomf.literal(session.getValueFactory() - .createValue(username)); - Constraint constraint = qomf.comparison(userIdDop, - QueryObjectModelFactory.JCR_OPERATOR_EQUAL_TO, userIdSop); - Query query = qomf.createQuery(userHomeSel, constraint, null, null); - return JcrUtils.querySingleNode(query); - } catch (RepositoryException e) { - throw new ArgeoJcrException( - "Cannot find profile for user " + username, e); - } - } - - /** Returns the home node of the session user or null if none was found. */ - public static Node getUserHome(Session session) { - String userID = session.getUserID(); - return getUserHome(session, userID); - } - - private UserJcrUtils() { - } -} diff --git a/org.argeo.server.jcr/src/org/argeo/jcr/VersionDiff.java b/org.argeo.server.jcr/src/org/argeo/jcr/VersionDiff.java deleted file mode 100644 index e6ae9130b..000000000 --- a/org.argeo.server.jcr/src/org/argeo/jcr/VersionDiff.java +++ /dev/null @@ -1,53 +0,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. - */ -package org.argeo.jcr; - -import java.util.Calendar; -import java.util.Map; - -/** - * Generic Object that enables the creation of history reports based on a JCR - * versionable node. userId and creation date are added to the map of - * PropertyDiff. - * - * These two fields might be null - * - */ -public class VersionDiff { - - private String userId; - private Map diffs; - private Calendar updateTime; - - public VersionDiff(String userId, Calendar updateTime, - Map diffs) { - this.userId = userId; - this.updateTime = updateTime; - this.diffs = diffs; - } - - public String getUserId() { - return userId; - } - - public Map getDiffs() { - return diffs; - } - - public Calendar getUpdateTime() { - return updateTime; - } -} diff --git a/org.argeo.server.jcr/src/org/argeo/jcr/argeo.cnd b/org.argeo.server.jcr/src/org/argeo/jcr/argeo.cnd deleted file mode 100644 index fbfea9dd9..000000000 --- a/org.argeo.server.jcr/src/org/argeo/jcr/argeo.cnd +++ /dev/null @@ -1,72 +0,0 @@ - - -// GENERIC TYPES NOT AVAILABLE IN JCR -[argeo:link] > mix:created, mix:lastModified -mixin -// URI(s) -- argeo:uri (STRING) m - -[argeo:references] > nt:unstructured -- * (REFERENCE) * - -// DATA MODEL -[argeo:dataModel] > mix:created, mix:lastModified, mix:versionable -mixin -- argeo:uri (STRING) m -- argeo:dataModelVersion (STRING) m - -// USER NODES -// user should be lower case, between 3 and 15 characters long -[argeo:userHome] > mix:created, mix:lastModified -mixin -- argeo:userID (STRING) m -- argeo:remoteRoles (STRING) * -// deprecated. for backward compatibility: -+ argeo:profile (argeo:userProfile) -+ argeo:keyring (argeo:pbeSpec) -+ argeo:preferences (argeo:preferenceNode) - -[argeo:userProfile] > mix:created, mix:lastModified, mix:title, mix:versionable -mixin -- argeo:userID (STRING) m -- argeo:enabled (BOOLEAN) -- argeo:accountNonExpired (BOOLEAN) -- argeo:accountNonLocked (BOOLEAN) -- argeo:credentialsNonExpired (BOOLEAN) - -[argeo:preferenceNode] > mix:lastModified, mix:versionable -mixin -+ * (argeo:preferenceNode) * version - -[argeo:remoteRepository] > nt:unstructured -- argeo:uri (STRING) -- argeo:userID (STRING) -+ argeo:password (argeo:encrypted) - -// TABULAR CONTENT -[argeo:table] > nt:file -+ * (argeo:column) * - -[argeo:column] > mix:title -- jcr:requiredType (STRING) = 'STRING' - -[argeo:csv] > nt:resource - -// CRYPTO -[argeo:encrypted] > nt:base -mixin -// initialization vector used by some algorithms -- argeo:iv (BINARY) - -[argeo:pbeKeySpec] > nt:base -mixin -- argeo:secretKeyFactory (STRING) -- argeo:salt (BINARY) -- argeo:iterationCount (LONG) -- argeo:keyLength (LONG) -- argeo:secretKeyEncryption (STRING) - -[argeo:pbeSpec] > argeo:pbeKeySpec -mixin -- argeo:cipher (STRING) - diff --git a/org.argeo.server.jcr/src/org/argeo/jcr/docbook/DocBookModel.java b/org.argeo.server.jcr/src/org/argeo/jcr/docbook/DocBookModel.java deleted file mode 100644 index 61a902d9e..000000000 --- a/org.argeo.server.jcr/src/org/argeo/jcr/docbook/DocBookModel.java +++ /dev/null @@ -1,42 +0,0 @@ -package org.argeo.jcr.docbook; - -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; - -import javax.jcr.ImportUUIDBehavior; -import javax.jcr.RepositoryException; -import javax.jcr.Session; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - -public class DocBookModel { - private final static Log log = LogFactory.getLog(DocBookModel.class); - private Session session; - - public DocBookModel(Session session) { - super(); - this.session = session; - } - - public void setSession(Session session) { - this.session = session; - } - - public void importXml(String path, InputStream in) - throws RepositoryException, IOException { - long begin = System.currentTimeMillis(); - session.importXML(path, in, - ImportUUIDBehavior.IMPORT_UUID_COLLISION_REPLACE_EXISTING); - long duration = System.currentTimeMillis() - begin; - if (log.isTraceEnabled()) - log.trace("Imported " + path + " in " + duration + " ms"); - - } - - public void exportXml(String path, OutputStream out) - throws RepositoryException, IOException { - session.exportDocumentView(path, out, true, false); - } -} diff --git a/org.argeo.server.jcr/src/org/argeo/jcr/docbook/DocBookNames.java b/org.argeo.server.jcr/src/org/argeo/jcr/docbook/DocBookNames.java deleted file mode 100644 index f4edb12b3..000000000 --- a/org.argeo.server.jcr/src/org/argeo/jcr/docbook/DocBookNames.java +++ /dev/null @@ -1,7 +0,0 @@ -package org.argeo.jcr.docbook; - -public interface DocBookNames { - public final static String DBK_ = "dbk:"; - public final static String DBK_PARA = DBK_ + "para"; - public final static String DBK_SECTION = DBK_ + "section"; -} diff --git a/org.argeo.server.jcr/src/org/argeo/jcr/docbook/docbook-full.cnd b/org.argeo.server.jcr/src/org/argeo/jcr/docbook/docbook-full.cnd deleted file mode 100644 index 7e4fefab9..000000000 --- a/org.argeo.server.jcr/src/org/argeo/jcr/docbook/docbook-full.cnd +++ /dev/null @@ -1,3005 +0,0 @@ - - - - - - - - -[argeodbk:titled] -mixin - + dbk:info (dbk:info) = dbk:info * - + dbk:title (dbk:title) = dbk:title * - + dbk:titleabbrev (dbk:titleabbrev) = dbk:titleabbrev * - -[argeodbk:linkingAttributes] -mixin - - dbk:linkend (String) - - xlink:actuate (String) - - xlink:arcrole (String) - - xlink:href (String) - - xlink:role (String) - - xlink:show (String) - - xlink:title (String) - - xlink:type (String) - -[argeodbk:freeText] -mixin - + dbk:phrase (dbk:phrase) = dbk:phrase * - + dbk:replaceable (dbk:replaceable) = dbk:replaceable * - + jcr:xmltext (dbk:xmltext) = dbk:xmltext * - -[argeodbk:markupInlines] -mixin - + dbk:code (dbk:code) = dbk:code * - + dbk:constant (dbk:constant) = dbk:constant * - + dbk:email (dbk:email) = dbk:email * - + dbk:literal (dbk:literal) = dbk:literal * - + dbk:markup (dbk:markup) = dbk:markup * - + dbk:symbol (dbk:symbol) = dbk:symbol * - + dbk:tag (dbk:tag) = dbk:tag * - + dbk:token (dbk:token) = dbk:token * - + dbk:uri (dbk:uri) = dbk:uri * - -[argeodbk:listElements] -mixin - + dbk:bibliolist (dbk:bibliolist) = dbk:bibliolist * - + dbk:calloutlist (dbk:calloutlist) = dbk:calloutlist * - + dbk:glosslist (dbk:glosslist) = dbk:glosslist * - + dbk:itemizedlist (dbk:itemizedlist) = dbk:itemizedlist * - + dbk:orderedlist (dbk:orderedlist) = dbk:orderedlist * - + dbk:procedure (dbk:procedure) = dbk:procedure * - + dbk:qandaset (dbk:qandaset) = dbk:qandaset * - + dbk:segmentedlist (dbk:segmentedlist) = dbk:segmentedlist * - + dbk:simplelist (dbk:simplelist) = dbk:simplelist * - + dbk:variablelist (dbk:variablelist) = dbk:variablelist * - -[argeodbk:paragraphElements] -mixin - + dbk:formalpara (dbk:formalpara) = dbk:formalpara * - + dbk:para (dbk:para) = dbk:para * - + dbk:simpara (dbk:simpara) = dbk:simpara * - -[argeodbk:indexingInlines] -mixin - + dbk:indexterm (dbk:indexterm) = dbk:indexterm * - -[argeodbk:techDocElements] -mixin - + dbk:caution (dbk:caution) = dbk:caution * - + dbk:classsynopsis (dbk:classsynopsis) = dbk:classsynopsis * - + dbk:cmdsynopsis (dbk:cmdsynopsis) = dbk:cmdsynopsis * - + dbk:constraintdef (dbk:constraintdef) = dbk:constraintdef * - + dbk:constructorsynopsis (dbk:constructorsynopsis) = dbk:constructorsynopsis * - + dbk:destructorsynopsis (dbk:destructorsynopsis) = dbk:destructorsynopsis * - + dbk:equation (dbk:equation) = dbk:equation * - + dbk:example (dbk:example) = dbk:example * - + dbk:fieldsynopsis (dbk:fieldsynopsis) = dbk:fieldsynopsis * - + dbk:figure (dbk:figure) = dbk:figure * - + dbk:funcsynopsis (dbk:funcsynopsis) = dbk:funcsynopsis * - + dbk:important (dbk:important) = dbk:important * - + dbk:informalequation (dbk:informalequation) = dbk:informalequation * - + dbk:informalexample (dbk:informalexample) = dbk:informalexample * - + dbk:informalfigure (dbk:informalfigure) = dbk:informalfigure * - + dbk:informaltable (dbk:informaltable) = dbk:informaltable * - + dbk:literallayout (dbk:literallayout) = dbk:literallayout * - + dbk:methodsynopsis (dbk:methodsynopsis) = dbk:methodsynopsis * - + dbk:msgset (dbk:msgset) = dbk:msgset * - + dbk:note (dbk:note) = dbk:note * - + dbk:productionset (dbk:productionset) = dbk:productionset * - + dbk:programlisting (dbk:programlisting) = dbk:programlisting * - + dbk:programlistingco (dbk:programlistingco) = dbk:programlistingco * - + dbk:screen (dbk:screen) = dbk:screen * - + dbk:screenco (dbk:screenco) = dbk:screenco * - + dbk:synopsis (dbk:synopsis) = dbk:synopsis * - + dbk:table (dbk:table) = dbk:table * - + dbk:task (dbk:task) = dbk:task * - + dbk:tip (dbk:tip) = dbk:tip * - + dbk:warning (dbk:warning) = dbk:warning * - -[argeodbk:techDocInlines] -mixin - + dbk:accel (dbk:accel) = dbk:accel * - + dbk:application (dbk:application) = dbk:application * - + dbk:classname (dbk:classname) = dbk:classname * - + dbk:command (dbk:command) = dbk:command * - + dbk:computeroutput (dbk:computeroutput) = dbk:computeroutput * - + dbk:database (dbk:database) = dbk:database * - + dbk:envar (dbk:envar) = dbk:envar * - + dbk:errorcode (dbk:errorcode) = dbk:errorcode * - + dbk:errorname (dbk:errorname) = dbk:errorname * - + dbk:errortext (dbk:errortext) = dbk:errortext * - + dbk:errortype (dbk:errortype) = dbk:errortype * - + dbk:exceptionname (dbk:exceptionname) = dbk:exceptionname * - + dbk:filename (dbk:filename) = dbk:filename * - + dbk:function (dbk:function) = dbk:function * - + dbk:guibutton (dbk:guibutton) = dbk:guibutton * - + dbk:guiicon (dbk:guiicon) = dbk:guiicon * - + dbk:guilabel (dbk:guilabel) = dbk:guilabel * - + dbk:guimenu (dbk:guimenu) = dbk:guimenu * - + dbk:guimenuitem (dbk:guimenuitem) = dbk:guimenuitem * - + dbk:guisubmenu (dbk:guisubmenu) = dbk:guisubmenu * - + dbk:hardware (dbk:hardware) = dbk:hardware * - + dbk:initializer (dbk:initializer) = dbk:initializer * - + dbk:inlineequation (dbk:inlineequation) = dbk:inlineequation * - + dbk:interfacename (dbk:interfacename) = dbk:interfacename * - + dbk:keycap (dbk:keycap) = dbk:keycap * - + dbk:keycode (dbk:keycode) = dbk:keycode * - + dbk:keycombo (dbk:keycombo) = dbk:keycombo * - + dbk:keysym (dbk:keysym) = dbk:keysym * - + dbk:menuchoice (dbk:menuchoice) = dbk:menuchoice * - + dbk:methodname (dbk:methodname) = dbk:methodname * - + dbk:modifier (dbk:modifier) = dbk:modifier * - + dbk:mousebutton (dbk:mousebutton) = dbk:mousebutton * - + dbk:nonterminal (dbk:nonterminal) = dbk:nonterminal * - + dbk:ooclass (dbk:ooclass) = dbk:ooclass * - + dbk:ooexception (dbk:ooexception) = dbk:ooexception * - + dbk:oointerface (dbk:oointerface) = dbk:oointerface * - + dbk:option (dbk:option) = dbk:option * - + dbk:optional (dbk:optional) = dbk:optional * - + dbk:package (dbk:package) = dbk:package * - + dbk:parameter (dbk:parameter) = dbk:parameter * - + dbk:productname (dbk:productname) = dbk:productname * - + dbk:productnumber (dbk:productnumber) = dbk:productnumber * - + dbk:prompt (dbk:prompt) = dbk:prompt * - + dbk:property (dbk:property) = dbk:property * - + dbk:returnvalue (dbk:returnvalue) = dbk:returnvalue * - + dbk:shortcut (dbk:shortcut) = dbk:shortcut * - + dbk:systemitem (dbk:systemitem) = dbk:systemitem * - + dbk:termdef (dbk:termdef) = dbk:termdef * - + dbk:trademark (dbk:trademark) = dbk:trademark * - + dbk:type (dbk:type) = dbk:type * - + dbk:userinput (dbk:userinput) = dbk:userinput * - + dbk:varname (dbk:varname) = dbk:varname * - -[argeodbk:publishingElements] -mixin - + dbk:address (dbk:address) = dbk:address * - + dbk:blockquote (dbk:blockquote) = dbk:blockquote * - + dbk:epigraph (dbk:epigraph) = dbk:epigraph * - + dbk:sidebar (dbk:sidebar) = dbk:sidebar * - -[argeodbk:ubiquitousInlines] -mixin - + dbk:alt (dbk:alt) = dbk:alt * - + dbk:anchor (dbk:anchor) = dbk:anchor * - + dbk:annotation (dbk:annotation) = dbk:annotation * - + dbk:biblioref (dbk:biblioref) = dbk:biblioref * - + dbk:inlinemediaobject (dbk:inlinemediaobject) = dbk:inlinemediaobject * - + dbk:link (dbk:link) = dbk:link * - + dbk:olink (dbk:olink) = dbk:olink * - + dbk:remark (dbk:remark) = dbk:remark * - + dbk:subscript (dbk:subscript) = dbk:subscript * - + dbk:superscript (dbk:superscript) = dbk:superscript * - + dbk:xref (dbk:xref) = dbk:xref * - -[argeodbk:abstractSection] -mixin - + dbk:annotation (dbk:annotation) = dbk:annotation * - + dbk:bibliography (dbk:bibliography) = dbk:bibliography * - + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead * - + dbk:glossary (dbk:glossary) = dbk:glossary * - + dbk:index (dbk:index) = dbk:index * - + dbk:remark (dbk:remark) = dbk:remark * - + dbk:revhistory (dbk:revhistory) = dbk:revhistory * - + dbk:subtitle (dbk:subtitle) = dbk:subtitle * - + dbk:toc (dbk:toc) = dbk:toc * - - dbk:label (String) - - dbk:status (String) - -[argeodbk:bibliographyInlines] -mixin - + dbk:author (dbk:author) = dbk:author * - + dbk:citation (dbk:citation) = dbk:citation * - + dbk:citebiblioid (dbk:citebiblioid) = dbk:citebiblioid * - + dbk:citerefentry (dbk:citerefentry) = dbk:citerefentry * - + dbk:citetitle (dbk:citetitle) = dbk:citetitle * - + dbk:editor (dbk:editor) = dbk:editor * - + dbk:jobtitle (dbk:jobtitle) = dbk:jobtitle * - + dbk:org (dbk:org) = dbk:org * - + dbk:orgname (dbk:orgname) = dbk:orgname * - + dbk:person (dbk:person) = dbk:person * - + dbk:personname (dbk:personname) = dbk:personname * - -[argeodbk:publishingInlines] -mixin - + dbk:abbrev (dbk:abbrev) = dbk:abbrev * - + dbk:acronym (dbk:acronym) = dbk:acronym * - + dbk:coref (dbk:coref) = dbk:coref * - + dbk:date (dbk:date) = dbk:date * - + dbk:emphasis (dbk:emphasis) = dbk:emphasis * - + dbk:firstterm (dbk:firstterm) = dbk:firstterm * - + dbk:footnote (dbk:footnote) = dbk:footnote * - + dbk:footnoteref (dbk:footnoteref) = dbk:footnoteref * - + dbk:foreignphrase (dbk:foreignphrase) = dbk:foreignphrase * - + dbk:glossterm (dbk:glossterm) = dbk:glossterm * - + dbk:quote (dbk:quote) = dbk:quote * - + dbk:wordasword (dbk:wordasword) = dbk:wordasword * - -[argeodbk:base] -abstract - - dbk:annotations (String) - - dbk:arch (String) - - dbk:audience (String) - - dbk:condition (String) - - dbk:conformance (String) - - dbk:dir (String) - - dbk:os (String) - - dbk:remap (String) - - dbk:revision (String) - - dbk:revisionflag (String) - - dbk:role (String) - - dbk:security (String) - - dbk:userlevel (String) - - dbk:vendor (String) - - dbk:version (String) - - dbk:wordsize (String) - - dbk:xreflabel (String) - - xml:base (String) - - xml:id (String) - - xml:lang (String) - -[dbk:abbrev] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines - + dbk:trademark (dbk:trademark) = dbk:trademark * - -[dbk:abstract] > argeodbk:base, argeodbk:linkingAttributes, argeodbk:paragraphElements, argeodbk:titled - + dbk:anchor (dbk:anchor) = dbk:anchor * - -[dbk:accel] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines - -[dbk:acknowledgements] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled - + dbk:anchor (dbk:anchor) = dbk:anchor * - + dbk:annotation (dbk:annotation) = dbk:annotation * - + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead * - + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject * - + dbk:remark (dbk:remark) = dbk:remark * - + dbk:revhistory (dbk:revhistory) = dbk:revhistory * - + dbk:screenshot (dbk:screenshot) = dbk:screenshot * - + dbk:subtitle (dbk:subtitle) = dbk:subtitle * - - dbk:label (String) - - dbk:status (String) - -[dbk:acronym] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines - + dbk:trademark (dbk:trademark) = dbk:trademark * - -[dbk:address] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines - + dbk:city (dbk:city) = dbk:city * - + dbk:country (dbk:country) = dbk:country * - + dbk:email (dbk:email) = dbk:email * - + dbk:fax (dbk:fax) = dbk:fax * - + dbk:otheraddr (dbk:otheraddr) = dbk:otheraddr * - + dbk:personname (dbk:personname) = dbk:personname * - + dbk:phone (dbk:phone) = dbk:phone * - + dbk:pob (dbk:pob) = dbk:pob * - + dbk:postcode (dbk:postcode) = dbk:postcode * - + dbk:state (dbk:state) = dbk:state * - + dbk:street (dbk:street) = dbk:street * - + dbk:uri (dbk:uri) = dbk:uri * - - dbk:continuation (String) - - dbk:language (String) - - dbk:linenumbering (String) - - dbk:startinglinenumber (String) - - xml:space (String) - -[dbk:affiliation] > argeodbk:base, argeodbk:linkingAttributes - + dbk:address (dbk:address) = dbk:address * - + dbk:jobtitle (dbk:jobtitle) = dbk:jobtitle * - + dbk:org (dbk:org) = dbk:org - + dbk:orgdiv (dbk:orgdiv) = dbk:orgdiv * - + dbk:orgname (dbk:orgname) = dbk:orgname - + dbk:shortaffil (dbk:shortaffil) = dbk:shortaffil - -[dbk:alt] > argeodbk:base - + dbk:inlinemediaobject (dbk:inlinemediaobject) = dbk:inlinemediaobject * - + jcr:xmltext (dbk:xmltext) = dbk:xmltext * - -[dbk:anchor] > argeodbk:base - -[dbk:annotation] > argeodbk:base, argeodbk:indexingInlines, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled - + dbk:anchor (dbk:anchor) = dbk:anchor * - + dbk:annotation (dbk:annotation) = dbk:annotation * - + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead * - + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject * - + dbk:remark (dbk:remark) = dbk:remark * - + dbk:revhistory (dbk:revhistory) = dbk:revhistory * - + dbk:screenshot (dbk:screenshot) = dbk:screenshot * - - dbk:annotates (String) - -[dbk:answer] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements - + dbk:anchor (dbk:anchor) = dbk:anchor * - + dbk:annotation (dbk:annotation) = dbk:annotation * - + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead * - + dbk:label (dbk:label) = dbk:label - + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject * - + dbk:remark (dbk:remark) = dbk:remark * - + dbk:revhistory (dbk:revhistory) = dbk:revhistory * - + dbk:screenshot (dbk:screenshot) = dbk:screenshot * - -[dbk:appendix] > argeodbk:abstractSection, argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled - + dbk:anchor (dbk:anchor) = dbk:anchor * - + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject * - + dbk:refentry (dbk:refentry) = dbk:refentry * - + dbk:screenshot (dbk:screenshot) = dbk:screenshot * - + dbk:sect1 (dbk:sect1) = dbk:sect1 * - + dbk:section (dbk:section) = dbk:section * - + dbk:simplesect (dbk:simplesect) = dbk:simplesect * - + dbk:simplesect (dbk:simplesect) = dbk:simplesect * - -[dbk:application] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines - - dbk:class (String) - -[dbk:arc] > argeodbk:base - - xlink:from (String) - - xlink:to (String) - -[dbk:area] > argeodbk:base - + dbk:alt (dbk:alt) = dbk:alt - - dbk:coords (String) - - dbk:label (String) - - dbk:linkends (String) - - dbk:otherunits (String) - - dbk:units (String) - -[dbk:areaset] > argeodbk:base - + dbk:area (dbk:area) = dbk:area * - - dbk:label (String) - - dbk:linkends (String) - - dbk:otherunits (String) - - dbk:units (String) - -[dbk:areaspec] > argeodbk:base, argeodbk:linkingAttributes - + dbk:area (dbk:area) = dbk:area * - + dbk:areaset (dbk:areaset) = dbk:areaset * - - dbk:otherunits (String) - - dbk:units (String) - -[dbk:arg] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines - + dbk:arg (dbk:arg) = dbk:arg * - + dbk:group (dbk:group) = dbk:group * - + dbk:option (dbk:option) = dbk:option * - + dbk:sbr (dbk:sbr) = dbk:sbr * - + dbk:synopfragmentref (dbk:synopfragmentref) = dbk:synopfragmentref * - - dbk:choice (String) - - dbk:rep (String) - -[dbk:article] > argeodbk:abstractSection, argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled - + dbk:acknowledgements (dbk:acknowledgements) = dbk:acknowledgements * - + dbk:anchor (dbk:anchor) = dbk:anchor * - + dbk:appendix (dbk:appendix) = dbk:appendix * - + dbk:colophon (dbk:colophon) = dbk:colophon * - + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject * - + dbk:refentry (dbk:refentry) = dbk:refentry * - + dbk:screenshot (dbk:screenshot) = dbk:screenshot * - + dbk:sect1 (dbk:sect1) = dbk:sect1 * - + dbk:section (dbk:section) = dbk:section * - + dbk:simplesect (dbk:simplesect) = dbk:simplesect * - + dbk:simplesect (dbk:simplesect) = dbk:simplesect * - - dbk:class (String) - -[dbk:artpagenums] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines - -[dbk:attribution] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines - + dbk:citation (dbk:citation) = dbk:citation * - + dbk:citetitle (dbk:citetitle) = dbk:citetitle * - + dbk:person (dbk:person) = dbk:person * - + dbk:personname (dbk:personname) = dbk:personname * - -[dbk:audiodata] > argeodbk:base - + dbk:info (dbk:info) = dbk:info - - dbk:entityref (String) - - dbk:fileref (String) - - dbk:format (String) - -[dbk:audioobject] > argeodbk:base, argeodbk:linkingAttributes - + dbk:audiodata (dbk:audiodata) = dbk:audiodata - + dbk:info (dbk:info) = dbk:info - -[dbk:author] > argeodbk:base, argeodbk:linkingAttributes - + dbk:address (dbk:address) = dbk:address * - + dbk:affiliation (dbk:affiliation) = dbk:affiliation * - + dbk:contrib (dbk:contrib) = dbk:contrib * - + dbk:email (dbk:email) = dbk:email * - + dbk:orgdiv (dbk:orgdiv) = dbk:orgdiv * - + dbk:orgname (dbk:orgname) = dbk:orgname - + dbk:personblurb (dbk:personblurb) = dbk:personblurb * - + dbk:personname (dbk:personname) = dbk:personname - + dbk:uri (dbk:uri) = dbk:uri * - -[dbk:authorgroup] > argeodbk:base, argeodbk:linkingAttributes - + dbk:author (dbk:author) = dbk:author * - + dbk:editor (dbk:editor) = dbk:editor * - + dbk:othercredit (dbk:othercredit) = dbk:othercredit * - -[dbk:authorinitials] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines - -[dbk:bibliocoverage] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines - - dbk:otherspatial (String) - - dbk:othertemporal (String) - - dbk:spatial (String) - - dbk:temporal (String) - -[dbk:bibliodiv] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled - + dbk:anchor (dbk:anchor) = dbk:anchor * - + dbk:annotation (dbk:annotation) = dbk:annotation * - + dbk:biblioentry (dbk:biblioentry) = dbk:biblioentry * - + dbk:bibliomixed (dbk:bibliomixed) = dbk:bibliomixed * - + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead * - + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject * - + dbk:remark (dbk:remark) = dbk:remark * - + dbk:revhistory (dbk:revhistory) = dbk:revhistory * - + dbk:screenshot (dbk:screenshot) = dbk:screenshot * - + dbk:subtitle (dbk:subtitle) = dbk:subtitle * - - dbk:label (String) - - dbk:status (String) - -[dbk:biblioentry] > argeodbk:base, argeodbk:linkingAttributes, argeodbk:publishingInlines - + dbk:abstract (dbk:abstract) = dbk:abstract * - + dbk:address (dbk:address) = dbk:address * - + dbk:annotation (dbk:annotation) = dbk:annotation * - + dbk:artpagenums (dbk:artpagenums) = dbk:artpagenums * - + dbk:author (dbk:author) = dbk:author * - + dbk:authorgroup (dbk:authorgroup) = dbk:authorgroup * - + dbk:authorinitials (dbk:authorinitials) = dbk:authorinitials * - + dbk:bibliocoverage (dbk:bibliocoverage) = dbk:bibliocoverage * - + dbk:biblioid (dbk:biblioid) = dbk:biblioid * - + dbk:bibliomisc (dbk:bibliomisc) = dbk:bibliomisc * - + dbk:bibliomset (dbk:bibliomset) = dbk:bibliomset * - + dbk:bibliorelation (dbk:bibliorelation) = dbk:bibliorelation * - + dbk:biblioset (dbk:biblioset) = dbk:biblioset * - + dbk:bibliosource (dbk:bibliosource) = dbk:bibliosource * - + dbk:citebiblioid (dbk:citebiblioid) = dbk:citebiblioid * - + dbk:citerefentry (dbk:citerefentry) = dbk:citerefentry * - + dbk:citetitle (dbk:citetitle) = dbk:citetitle * - + dbk:collab (dbk:collab) = dbk:collab * - + dbk:confgroup (dbk:confgroup) = dbk:confgroup * - + dbk:contractnum (dbk:contractnum) = dbk:contractnum * - + dbk:contractsponsor (dbk:contractsponsor) = dbk:contractsponsor * - + dbk:copyright (dbk:copyright) = dbk:copyright * - + dbk:cover (dbk:cover) = dbk:cover * - + dbk:edition (dbk:edition) = dbk:edition * - + dbk:editor (dbk:editor) = dbk:editor * - + dbk:extendedlink (dbk:extendedlink) = dbk:extendedlink * - + dbk:issuenum (dbk:issuenum) = dbk:issuenum * - + dbk:itermset (dbk:itermset) = dbk:itermset * - + dbk:keywordset (dbk:keywordset) = dbk:keywordset * - + dbk:legalnotice (dbk:legalnotice) = dbk:legalnotice * - + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject * - + dbk:org (dbk:org) = dbk:org * - + dbk:orgname (dbk:orgname) = dbk:orgname * - + dbk:othercredit (dbk:othercredit) = dbk:othercredit * - + dbk:pagenums (dbk:pagenums) = dbk:pagenums * - + dbk:person (dbk:person) = dbk:person * - + dbk:personblurb (dbk:personblurb) = dbk:personblurb * - + dbk:personname (dbk:personname) = dbk:personname * - + dbk:phrase (dbk:phrase) = dbk:phrase * - + dbk:printhistory (dbk:printhistory) = dbk:printhistory * - + dbk:productname (dbk:productname) = dbk:productname * - + dbk:productnumber (dbk:productnumber) = dbk:productnumber * - + dbk:pubdate (dbk:pubdate) = dbk:pubdate * - + dbk:publisher (dbk:publisher) = dbk:publisher * - + dbk:publishername (dbk:publishername) = dbk:publishername * - + dbk:releaseinfo (dbk:releaseinfo) = dbk:releaseinfo * - + dbk:revhistory (dbk:revhistory) = dbk:revhistory * - + dbk:seriesvolnums (dbk:seriesvolnums) = dbk:seriesvolnums * - + dbk:subjectset (dbk:subjectset) = dbk:subjectset * - + dbk:subscript (dbk:subscript) = dbk:subscript * - + dbk:subtitle (dbk:subtitle) = dbk:subtitle * - + dbk:superscript (dbk:superscript) = dbk:superscript * - + dbk:title (dbk:title) = dbk:title * - + dbk:titleabbrev (dbk:titleabbrev) = dbk:titleabbrev * - + dbk:volumenum (dbk:volumenum) = dbk:volumenum * - -[dbk:bibliography] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled - + dbk:anchor (dbk:anchor) = dbk:anchor * - + dbk:annotation (dbk:annotation) = dbk:annotation * - + dbk:bibliodiv (dbk:bibliodiv) = dbk:bibliodiv * - + dbk:biblioentry (dbk:biblioentry) = dbk:biblioentry * - + dbk:bibliomixed (dbk:bibliomixed) = dbk:bibliomixed * - + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead * - + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject * - + dbk:remark (dbk:remark) = dbk:remark * - + dbk:revhistory (dbk:revhistory) = dbk:revhistory * - + dbk:screenshot (dbk:screenshot) = dbk:screenshot * - + dbk:subtitle (dbk:subtitle) = dbk:subtitle * - - dbk:label (String) - - dbk:status (String) - -[dbk:biblioid] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines - - dbk:class (String) - - dbk:otherclass (String) - -[dbk:bibliolist] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled - + dbk:anchor (dbk:anchor) = dbk:anchor * - + dbk:annotation (dbk:annotation) = dbk:annotation * - + dbk:biblioentry (dbk:biblioentry) = dbk:biblioentry * - + dbk:bibliomixed (dbk:bibliomixed) = dbk:bibliomixed * - + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead * - + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject * - + dbk:remark (dbk:remark) = dbk:remark * - + dbk:revhistory (dbk:revhistory) = dbk:revhistory * - + dbk:screenshot (dbk:screenshot) = dbk:screenshot * - -[dbk:bibliomisc] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines - -[dbk:bibliomixed] > argeodbk:base, argeodbk:linkingAttributes, argeodbk:publishingInlines - + dbk:abstract (dbk:abstract) = dbk:abstract * - + dbk:address (dbk:address) = dbk:address * - + dbk:annotation (dbk:annotation) = dbk:annotation * - + dbk:artpagenums (dbk:artpagenums) = dbk:artpagenums * - + dbk:author (dbk:author) = dbk:author * - + dbk:authorgroup (dbk:authorgroup) = dbk:authorgroup * - + dbk:authorinitials (dbk:authorinitials) = dbk:authorinitials * - + dbk:bibliocoverage (dbk:bibliocoverage) = dbk:bibliocoverage * - + dbk:biblioid (dbk:biblioid) = dbk:biblioid * - + dbk:bibliomisc (dbk:bibliomisc) = dbk:bibliomisc * - + dbk:bibliomset (dbk:bibliomset) = dbk:bibliomset * - + dbk:bibliorelation (dbk:bibliorelation) = dbk:bibliorelation * - + dbk:biblioset (dbk:biblioset) = dbk:biblioset * - + dbk:bibliosource (dbk:bibliosource) = dbk:bibliosource * - + dbk:citebiblioid (dbk:citebiblioid) = dbk:citebiblioid * - + dbk:citerefentry (dbk:citerefentry) = dbk:citerefentry * - + dbk:citetitle (dbk:citetitle) = dbk:citetitle * - + dbk:collab (dbk:collab) = dbk:collab * - + dbk:confgroup (dbk:confgroup) = dbk:confgroup * - + dbk:contractnum (dbk:contractnum) = dbk:contractnum * - + dbk:contractsponsor (dbk:contractsponsor) = dbk:contractsponsor * - + dbk:copyright (dbk:copyright) = dbk:copyright * - + dbk:cover (dbk:cover) = dbk:cover * - + dbk:edition (dbk:edition) = dbk:edition * - + dbk:editor (dbk:editor) = dbk:editor * - + dbk:extendedlink (dbk:extendedlink) = dbk:extendedlink * - + dbk:issuenum (dbk:issuenum) = dbk:issuenum * - + dbk:itermset (dbk:itermset) = dbk:itermset * - + dbk:keywordset (dbk:keywordset) = dbk:keywordset * - + dbk:legalnotice (dbk:legalnotice) = dbk:legalnotice * - + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject * - + dbk:org (dbk:org) = dbk:org * - + dbk:orgname (dbk:orgname) = dbk:orgname * - + dbk:othercredit (dbk:othercredit) = dbk:othercredit * - + dbk:pagenums (dbk:pagenums) = dbk:pagenums * - + dbk:person (dbk:person) = dbk:person * - + dbk:personblurb (dbk:personblurb) = dbk:personblurb * - + dbk:personname (dbk:personname) = dbk:personname * - + dbk:phrase (dbk:phrase) = dbk:phrase * - + dbk:printhistory (dbk:printhistory) = dbk:printhistory * - + dbk:productname (dbk:productname) = dbk:productname * - + dbk:productnumber (dbk:productnumber) = dbk:productnumber * - + dbk:pubdate (dbk:pubdate) = dbk:pubdate * - + dbk:publisher (dbk:publisher) = dbk:publisher * - + dbk:publishername (dbk:publishername) = dbk:publishername * - + dbk:releaseinfo (dbk:releaseinfo) = dbk:releaseinfo * - + dbk:revhistory (dbk:revhistory) = dbk:revhistory * - + dbk:seriesvolnums (dbk:seriesvolnums) = dbk:seriesvolnums * - + dbk:subjectset (dbk:subjectset) = dbk:subjectset * - + dbk:subscript (dbk:subscript) = dbk:subscript * - + dbk:subtitle (dbk:subtitle) = dbk:subtitle * - + dbk:superscript (dbk:superscript) = dbk:superscript * - + dbk:title (dbk:title) = dbk:title * - + dbk:titleabbrev (dbk:titleabbrev) = dbk:titleabbrev * - + dbk:volumenum (dbk:volumenum) = dbk:volumenum * - + jcr:xmltext (dbk:xmltext) = dbk:xmltext * - -[dbk:bibliomset] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:publishingInlines, argeodbk:ubiquitousInlines - + dbk:abstract (dbk:abstract) = dbk:abstract * - + dbk:address (dbk:address) = dbk:address * - + dbk:artpagenums (dbk:artpagenums) = dbk:artpagenums * - + dbk:author (dbk:author) = dbk:author * - + dbk:authorgroup (dbk:authorgroup) = dbk:authorgroup * - + dbk:authorinitials (dbk:authorinitials) = dbk:authorinitials * - + dbk:bibliocoverage (dbk:bibliocoverage) = dbk:bibliocoverage * - + dbk:biblioid (dbk:biblioid) = dbk:biblioid * - + dbk:bibliomisc (dbk:bibliomisc) = dbk:bibliomisc * - + dbk:bibliomset (dbk:bibliomset) = dbk:bibliomset * - + dbk:bibliorelation (dbk:bibliorelation) = dbk:bibliorelation * - + dbk:biblioset (dbk:biblioset) = dbk:biblioset * - + dbk:bibliosource (dbk:bibliosource) = dbk:bibliosource * - + dbk:citebiblioid (dbk:citebiblioid) = dbk:citebiblioid * - + dbk:citerefentry (dbk:citerefentry) = dbk:citerefentry * - + dbk:citetitle (dbk:citetitle) = dbk:citetitle * - + dbk:collab (dbk:collab) = dbk:collab * - + dbk:confgroup (dbk:confgroup) = dbk:confgroup * - + dbk:contractnum (dbk:contractnum) = dbk:contractnum * - + dbk:contractsponsor (dbk:contractsponsor) = dbk:contractsponsor * - + dbk:copyright (dbk:copyright) = dbk:copyright * - + dbk:cover (dbk:cover) = dbk:cover * - + dbk:edition (dbk:edition) = dbk:edition * - + dbk:editor (dbk:editor) = dbk:editor * - + dbk:extendedlink (dbk:extendedlink) = dbk:extendedlink * - + dbk:issuenum (dbk:issuenum) = dbk:issuenum * - + dbk:itermset (dbk:itermset) = dbk:itermset * - + dbk:keywordset (dbk:keywordset) = dbk:keywordset * - + dbk:legalnotice (dbk:legalnotice) = dbk:legalnotice * - + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject * - + dbk:org (dbk:org) = dbk:org * - + dbk:orgname (dbk:orgname) = dbk:orgname * - + dbk:othercredit (dbk:othercredit) = dbk:othercredit * - + dbk:pagenums (dbk:pagenums) = dbk:pagenums * - + dbk:person (dbk:person) = dbk:person * - + dbk:personblurb (dbk:personblurb) = dbk:personblurb * - + dbk:personname (dbk:personname) = dbk:personname * - + dbk:printhistory (dbk:printhistory) = dbk:printhistory * - + dbk:productname (dbk:productname) = dbk:productname * - + dbk:productnumber (dbk:productnumber) = dbk:productnumber * - + dbk:pubdate (dbk:pubdate) = dbk:pubdate * - + dbk:publisher (dbk:publisher) = dbk:publisher * - + dbk:publishername (dbk:publishername) = dbk:publishername * - + dbk:releaseinfo (dbk:releaseinfo) = dbk:releaseinfo * - + dbk:revhistory (dbk:revhistory) = dbk:revhistory * - + dbk:seriesvolnums (dbk:seriesvolnums) = dbk:seriesvolnums * - + dbk:subjectset (dbk:subjectset) = dbk:subjectset * - + dbk:subtitle (dbk:subtitle) = dbk:subtitle * - + dbk:title (dbk:title) = dbk:title * - + dbk:titleabbrev (dbk:titleabbrev) = dbk:titleabbrev * - + dbk:volumenum (dbk:volumenum) = dbk:volumenum * - - dbk:relation (String) - -[dbk:biblioref] > argeodbk:base, argeodbk:linkingAttributes - - dbk:begin (String) - - dbk:end (String) - - dbk:endterm (Reference) - - dbk:units (String) - - dbk:xrefstyle (String) - -[dbk:bibliorelation] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines - - dbk:class (String) - - dbk:otherclass (String) - - dbk:othertype (String) - - dbk:type (String) - -[dbk:biblioset] > argeodbk:base, argeodbk:linkingAttributes, argeodbk:publishingInlines - + dbk:abstract (dbk:abstract) = dbk:abstract * - + dbk:address (dbk:address) = dbk:address * - + dbk:annotation (dbk:annotation) = dbk:annotation * - + dbk:artpagenums (dbk:artpagenums) = dbk:artpagenums * - + dbk:author (dbk:author) = dbk:author * - + dbk:authorgroup (dbk:authorgroup) = dbk:authorgroup * - + dbk:authorinitials (dbk:authorinitials) = dbk:authorinitials * - + dbk:bibliocoverage (dbk:bibliocoverage) = dbk:bibliocoverage * - + dbk:biblioid (dbk:biblioid) = dbk:biblioid * - + dbk:bibliomisc (dbk:bibliomisc) = dbk:bibliomisc * - + dbk:bibliomset (dbk:bibliomset) = dbk:bibliomset * - + dbk:bibliorelation (dbk:bibliorelation) = dbk:bibliorelation * - + dbk:biblioset (dbk:biblioset) = dbk:biblioset * - + dbk:bibliosource (dbk:bibliosource) = dbk:bibliosource * - + dbk:citebiblioid (dbk:citebiblioid) = dbk:citebiblioid * - + dbk:citerefentry (dbk:citerefentry) = dbk:citerefentry * - + dbk:citetitle (dbk:citetitle) = dbk:citetitle * - + dbk:collab (dbk:collab) = dbk:collab * - + dbk:confgroup (dbk:confgroup) = dbk:confgroup * - + dbk:contractnum (dbk:contractnum) = dbk:contractnum * - + dbk:contractsponsor (dbk:contractsponsor) = dbk:contractsponsor * - + dbk:copyright (dbk:copyright) = dbk:copyright * - + dbk:cover (dbk:cover) = dbk:cover * - + dbk:edition (dbk:edition) = dbk:edition * - + dbk:editor (dbk:editor) = dbk:editor * - + dbk:extendedlink (dbk:extendedlink) = dbk:extendedlink * - + dbk:issuenum (dbk:issuenum) = dbk:issuenum * - + dbk:itermset (dbk:itermset) = dbk:itermset * - + dbk:keywordset (dbk:keywordset) = dbk:keywordset * - + dbk:legalnotice (dbk:legalnotice) = dbk:legalnotice * - + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject * - + dbk:org (dbk:org) = dbk:org * - + dbk:orgname (dbk:orgname) = dbk:orgname * - + dbk:othercredit (dbk:othercredit) = dbk:othercredit * - + dbk:pagenums (dbk:pagenums) = dbk:pagenums * - + dbk:person (dbk:person) = dbk:person * - + dbk:personblurb (dbk:personblurb) = dbk:personblurb * - + dbk:personname (dbk:personname) = dbk:personname * - + dbk:phrase (dbk:phrase) = dbk:phrase * - + dbk:printhistory (dbk:printhistory) = dbk:printhistory * - + dbk:productname (dbk:productname) = dbk:productname * - + dbk:productnumber (dbk:productnumber) = dbk:productnumber * - + dbk:pubdate (dbk:pubdate) = dbk:pubdate * - + dbk:publisher (dbk:publisher) = dbk:publisher * - + dbk:publishername (dbk:publishername) = dbk:publishername * - + dbk:releaseinfo (dbk:releaseinfo) = dbk:releaseinfo * - + dbk:revhistory (dbk:revhistory) = dbk:revhistory * - + dbk:seriesvolnums (dbk:seriesvolnums) = dbk:seriesvolnums * - + dbk:subjectset (dbk:subjectset) = dbk:subjectset * - + dbk:subscript (dbk:subscript) = dbk:subscript * - + dbk:subtitle (dbk:subtitle) = dbk:subtitle * - + dbk:superscript (dbk:superscript) = dbk:superscript * - + dbk:title (dbk:title) = dbk:title * - + dbk:titleabbrev (dbk:titleabbrev) = dbk:titleabbrev * - + dbk:volumenum (dbk:volumenum) = dbk:volumenum * - - dbk:relation (String) - -[dbk:bibliosource] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines - - dbk:class (String) - - dbk:otherclass (String) - -[dbk:blockquote] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled - + dbk:anchor (dbk:anchor) = dbk:anchor * - + dbk:annotation (dbk:annotation) = dbk:annotation * - + dbk:attribution (dbk:attribution) = dbk:attribution - + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead * - + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject * - + dbk:remark (dbk:remark) = dbk:remark * - + dbk:revhistory (dbk:revhistory) = dbk:revhistory * - + dbk:screenshot (dbk:screenshot) = dbk:screenshot * - -[dbk:book] > argeodbk:base, argeodbk:linkingAttributes, argeodbk:titled - + dbk:acknowledgements (dbk:acknowledgements) = dbk:acknowledgements * - + dbk:appendix (dbk:appendix) = dbk:appendix * - + dbk:article (dbk:article) = dbk:article * - + dbk:bibliography (dbk:bibliography) = dbk:bibliography * - + dbk:chapter (dbk:chapter) = dbk:chapter * - + dbk:colophon (dbk:colophon) = dbk:colophon * - + dbk:dedication (dbk:dedication) = dbk:dedication * - + dbk:glossary (dbk:glossary) = dbk:glossary * - + dbk:index (dbk:index) = dbk:index * - + dbk:part (dbk:part) = dbk:part * - + dbk:preface (dbk:preface) = dbk:preface * - + dbk:reference (dbk:reference) = dbk:reference * - + dbk:subtitle (dbk:subtitle) = dbk:subtitle * - + dbk:toc (dbk:toc) = dbk:toc * - - dbk:label (String) - - dbk:status (String) - -[dbk:bridgehead] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:markupInlines, argeodbk:publishingInlines, argeodbk:techDocInlines, argeodbk:ubiquitousInlines - - dbk:otherrenderas (String) - - dbk:renderas (String) - -[dbk:callout] > argeodbk:base, argeodbk:indexingInlines, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements - + dbk:anchor (dbk:anchor) = dbk:anchor * - + dbk:annotation (dbk:annotation) = dbk:annotation * - + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead * - + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject * - + dbk:remark (dbk:remark) = dbk:remark * - + dbk:revhistory (dbk:revhistory) = dbk:revhistory * - + dbk:screenshot (dbk:screenshot) = dbk:screenshot * - - dbk:arearefs (String) - -[dbk:calloutlist] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled - + dbk:anchor (dbk:anchor) = dbk:anchor * - + dbk:annotation (dbk:annotation) = dbk:annotation * - + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead * - + dbk:callout (dbk:callout) = dbk:callout * - + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject * - + dbk:remark (dbk:remark) = dbk:remark * - + dbk:revhistory (dbk:revhistory) = dbk:revhistory * - + dbk:screenshot (dbk:screenshot) = dbk:screenshot * - -[dbk:caption] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements - + dbk:anchor (dbk:anchor) = dbk:anchor * - + dbk:annotation (dbk:annotation) = dbk:annotation * - + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead * - + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject * - + dbk:remark (dbk:remark) = dbk:remark * - + dbk:revhistory (dbk:revhistory) = dbk:revhistory * - + dbk:screenshot (dbk:screenshot) = dbk:screenshot * - + jcr:xmltext (dbk:xmltext) = dbk:xmltext * - - dbk:class (String) - - dbk:lang (String) - - dbk:onclick (String) - - dbk:ondblclick (String) - - dbk:onkeydown (String) - - dbk:onkeypress (String) - - dbk:onkeyup (String) - - dbk:onmousedown (String) - - dbk:onmousemove (String) - - dbk:onmouseout (String) - - dbk:onmouseover (String) - - dbk:onmouseup (String) - - dbk:style (String) - - dbk:title (String) - -[dbk:caution] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled - + dbk:anchor (dbk:anchor) = dbk:anchor * - + dbk:annotation (dbk:annotation) = dbk:annotation * - + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead * - + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject * - + dbk:remark (dbk:remark) = dbk:remark * - + dbk:revhistory (dbk:revhistory) = dbk:revhistory * - + dbk:screenshot (dbk:screenshot) = dbk:screenshot * - -[dbk:chapter] > argeodbk:abstractSection, argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled - + dbk:anchor (dbk:anchor) = dbk:anchor * - + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject * - + dbk:refentry (dbk:refentry) = dbk:refentry * - + dbk:screenshot (dbk:screenshot) = dbk:screenshot * - + dbk:sect1 (dbk:sect1) = dbk:sect1 * - + dbk:section (dbk:section) = dbk:section * - + dbk:simplesect (dbk:simplesect) = dbk:simplesect * - + dbk:simplesect (dbk:simplesect) = dbk:simplesect * - -[dbk:citation] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:markupInlines, argeodbk:publishingInlines, argeodbk:techDocInlines, argeodbk:ubiquitousInlines - -[dbk:citebiblioid] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines - - dbk:class (String) - - dbk:otherclass (String) - -[dbk:citerefentry] > argeodbk:base, argeodbk:linkingAttributes - + dbk:manvolnum (dbk:manvolnum) = dbk:manvolnum - + dbk:refentrytitle (dbk:refentrytitle) = dbk:refentrytitle - -[dbk:citetitle] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:markupInlines, argeodbk:publishingInlines, argeodbk:techDocInlines, argeodbk:ubiquitousInlines - - dbk:pubwork (String) - -[dbk:city] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines - -[dbk:classname] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines - -[dbk:classsynopsis] > argeodbk:base, argeodbk:linkingAttributes - + dbk:classsynopsisinfo (dbk:classsynopsisinfo) = dbk:classsynopsisinfo * - + dbk:constructorsynopsis (dbk:constructorsynopsis) = dbk:constructorsynopsis * - + dbk:destructorsynopsis (dbk:destructorsynopsis) = dbk:destructorsynopsis * - + dbk:fieldsynopsis (dbk:fieldsynopsis) = dbk:fieldsynopsis * - + dbk:methodsynopsis (dbk:methodsynopsis) = dbk:methodsynopsis * - + dbk:ooclass (dbk:ooclass) = dbk:ooclass * - + dbk:ooexception (dbk:ooexception) = dbk:ooexception * - + dbk:oointerface (dbk:oointerface) = dbk:oointerface * - - dbk:class (String) - - dbk:language (String) - -[dbk:classsynopsisinfo] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:markupInlines, argeodbk:publishingInlines, argeodbk:techDocInlines, argeodbk:ubiquitousInlines - + dbk:co (dbk:co) = dbk:co * - + dbk:info (dbk:info) = dbk:info * - + dbk:lineannotation (dbk:lineannotation) = dbk:lineannotation * - + dbk:textobject (dbk:textobject) = dbk:textobject * - - dbk:continuation (String) - - dbk:language (String) - - dbk:linenumbering (String) - - dbk:startinglinenumber (String) - - xml:space (String) - -[dbk:cmdsynopsis] > argeodbk:base, argeodbk:linkingAttributes - + dbk:arg (dbk:arg) = dbk:arg * - + dbk:command (dbk:command) = dbk:command * - + dbk:group (dbk:group) = dbk:group * - + dbk:info (dbk:info) = dbk:info - + dbk:sbr (dbk:sbr) = dbk:sbr * - + dbk:synopfragment (dbk:synopfragment) = dbk:synopfragment * - - dbk:cmdlength (String) - - dbk:label (String) - - dbk:sepchar (String) - -[dbk:co] > argeodbk:base - - dbk:label (String) - - dbk:linkends (String) - -[dbk:code] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines - + dbk:classname (dbk:classname) = dbk:classname * - + dbk:exceptionname (dbk:exceptionname) = dbk:exceptionname * - + dbk:function (dbk:function) = dbk:function * - + dbk:initializer (dbk:initializer) = dbk:initializer * - + dbk:interfacename (dbk:interfacename) = dbk:interfacename * - + dbk:methodname (dbk:methodname) = dbk:methodname * - + dbk:modifier (dbk:modifier) = dbk:modifier * - + dbk:ooclass (dbk:ooclass) = dbk:ooclass * - + dbk:ooexception (dbk:ooexception) = dbk:ooexception * - + dbk:oointerface (dbk:oointerface) = dbk:oointerface * - + dbk:parameter (dbk:parameter) = dbk:parameter * - + dbk:returnvalue (dbk:returnvalue) = dbk:returnvalue * - + dbk:type (dbk:type) = dbk:type * - + dbk:varname (dbk:varname) = dbk:varname * - - dbk:language (String) - -[dbk:col] > nt:base - - dbk:align (String) - - dbk:annotations (String) - - dbk:arch (String) - - dbk:audience (String) - - dbk:char (String) - - dbk:charoff (String) - - dbk:class (String) - - dbk:condition (String) - - dbk:conformance (String) - - dbk:dir (String) - - dbk:lang (String) - - dbk:onclick (String) - - dbk:ondblclick (String) - - dbk:onkeydown (String) - - dbk:onkeypress (String) - - dbk:onkeyup (String) - - dbk:onmousedown (String) - - dbk:onmousemove (String) - - dbk:onmouseout (String) - - dbk:onmouseover (String) - - dbk:onmouseup (String) - - dbk:os (String) - - dbk:remap (String) - - dbk:revision (String) - - dbk:revisionflag (String) - - dbk:security (String) - - dbk:span (String) - - dbk:style (String) - - dbk:title (String) - - dbk:userlevel (String) - - dbk:valign (String) - - dbk:vendor (String) - - dbk:version (String) - - dbk:width (String) - - dbk:wordsize (String) - - dbk:xreflabel (String) - - xml:base (String) - - xml:id (String) - - xml:lang (String) - -[dbk:colgroup] > nt:base - + dbk:col (dbk:col) = dbk:col * - - dbk:align (String) - - dbk:annotations (String) - - dbk:arch (String) - - dbk:audience (String) - - dbk:char (String) - - dbk:charoff (String) - - dbk:class (String) - - dbk:condition (String) - - dbk:conformance (String) - - dbk:dir (String) - - dbk:lang (String) - - dbk:onclick (String) - - dbk:ondblclick (String) - - dbk:onkeydown (String) - - dbk:onkeypress (String) - - dbk:onkeyup (String) - - dbk:onmousedown (String) - - dbk:onmousemove (String) - - dbk:onmouseout (String) - - dbk:onmouseover (String) - - dbk:onmouseup (String) - - dbk:os (String) - - dbk:remap (String) - - dbk:revision (String) - - dbk:revisionflag (String) - - dbk:security (String) - - dbk:span (String) - - dbk:style (String) - - dbk:title (String) - - dbk:userlevel (String) - - dbk:valign (String) - - dbk:vendor (String) - - dbk:version (String) - - dbk:width (String) - - dbk:wordsize (String) - - dbk:xreflabel (String) - - xml:base (String) - - xml:id (String) - - xml:lang (String) - -[dbk:collab] > argeodbk:base, argeodbk:linkingAttributes - + dbk:affiliation (dbk:affiliation) = dbk:affiliation * - + dbk:org (dbk:org) = dbk:org * - + dbk:orgname (dbk:orgname) = dbk:orgname * - + dbk:person (dbk:person) = dbk:person * - + dbk:personname (dbk:personname) = dbk:personname * - -[dbk:colophon] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled - + dbk:anchor (dbk:anchor) = dbk:anchor * - + dbk:annotation (dbk:annotation) = dbk:annotation * - + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead * - + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject * - + dbk:remark (dbk:remark) = dbk:remark * - + dbk:revhistory (dbk:revhistory) = dbk:revhistory * - + dbk:screenshot (dbk:screenshot) = dbk:screenshot * - + dbk:subtitle (dbk:subtitle) = dbk:subtitle * - - dbk:label (String) - - dbk:status (String) - -[dbk:colspec] > argeodbk:base, argeodbk:linkingAttributes - - dbk:align (String) - - dbk:char (String) - - dbk:charoff (String) - - dbk:colname (String) - - dbk:colnum (String) - - dbk:colsep (String) - - dbk:colwidth (String) - - dbk:rowsep (String) - -[dbk:command] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines - -[dbk:computeroutput] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:markupInlines, argeodbk:ubiquitousInlines - + dbk:co (dbk:co) = dbk:co * - + dbk:command (dbk:command) = dbk:command * - + dbk:computeroutput (dbk:computeroutput) = dbk:computeroutput * - + dbk:envar (dbk:envar) = dbk:envar * - + dbk:filename (dbk:filename) = dbk:filename * - + dbk:nonterminal (dbk:nonterminal) = dbk:nonterminal * - + dbk:option (dbk:option) = dbk:option * - + dbk:optional (dbk:optional) = dbk:optional * - + dbk:package (dbk:package) = dbk:package * - + dbk:parameter (dbk:parameter) = dbk:parameter * - + dbk:prompt (dbk:prompt) = dbk:prompt * - + dbk:property (dbk:property) = dbk:property * - + dbk:replaceable (dbk:replaceable) = dbk:replaceable * - + dbk:systemitem (dbk:systemitem) = dbk:systemitem * - + dbk:termdef (dbk:termdef) = dbk:termdef * - + dbk:userinput (dbk:userinput) = dbk:userinput * - + jcr:xmltext (dbk:xmltext) = dbk:xmltext * - -[dbk:confdates] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines - -[dbk:confgroup] > argeodbk:base, argeodbk:linkingAttributes - + dbk:address (dbk:address) = dbk:address * - + dbk:confdates (dbk:confdates) = dbk:confdates * - + dbk:confnum (dbk:confnum) = dbk:confnum * - + dbk:confsponsor (dbk:confsponsor) = dbk:confsponsor * - + dbk:conftitle (dbk:conftitle) = dbk:conftitle * - -[dbk:confnum] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines - -[dbk:confsponsor] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines - -[dbk:conftitle] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines - -[dbk:constant] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines - - dbk:class (String) - -[dbk:constraint] > argeodbk:base, argeodbk:linkingAttributes - -[dbk:constraintdef] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled - + dbk:anchor (dbk:anchor) = dbk:anchor * - + dbk:annotation (dbk:annotation) = dbk:annotation * - + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead * - + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject * - + dbk:remark (dbk:remark) = dbk:remark * - + dbk:revhistory (dbk:revhistory) = dbk:revhistory * - + dbk:screenshot (dbk:screenshot) = dbk:screenshot * - -[dbk:constructorsynopsis] > argeodbk:base, argeodbk:linkingAttributes - + dbk:exceptionname (dbk:exceptionname) = dbk:exceptionname * - + dbk:methodname (dbk:methodname) = dbk:methodname - + dbk:methodparam (dbk:methodparam) = dbk:methodparam * - + dbk:modifier (dbk:modifier) = dbk:modifier * - + dbk:void (dbk:void) = dbk:void - - dbk:language (String) - -[dbk:contractnum] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines - -[dbk:contractsponsor] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines - -[dbk:contrib] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines - -[dbk:copyright] > argeodbk:base, argeodbk:linkingAttributes - + dbk:holder (dbk:holder) = dbk:holder * - + dbk:year (dbk:year) = dbk:year * - -[dbk:coref] > argeodbk:base, argeodbk:linkingAttributes - - dbk:label (String) - -[dbk:country] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines - -[dbk:cover] > argeodbk:base, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements - + dbk:anchor (dbk:anchor) = dbk:anchor * - + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead * - + dbk:classsynopsis (dbk:classsynopsis) = dbk:classsynopsis * - + dbk:cmdsynopsis (dbk:cmdsynopsis) = dbk:cmdsynopsis * - + dbk:constraintdef (dbk:constraintdef) = dbk:constraintdef * - + dbk:constructorsynopsis (dbk:constructorsynopsis) = dbk:constructorsynopsis * - + dbk:destructorsynopsis (dbk:destructorsynopsis) = dbk:destructorsynopsis * - + dbk:fieldsynopsis (dbk:fieldsynopsis) = dbk:fieldsynopsis * - + dbk:funcsynopsis (dbk:funcsynopsis) = dbk:funcsynopsis * - + dbk:informalequation (dbk:informalequation) = dbk:informalequation * - + dbk:informalexample (dbk:informalexample) = dbk:informalexample * - + dbk:informalfigure (dbk:informalfigure) = dbk:informalfigure * - + dbk:informaltable (dbk:informaltable) = dbk:informaltable * - + dbk:literallayout (dbk:literallayout) = dbk:literallayout * - + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject * - + dbk:methodsynopsis (dbk:methodsynopsis) = dbk:methodsynopsis * - + dbk:msgset (dbk:msgset) = dbk:msgset * - + dbk:productionset (dbk:productionset) = dbk:productionset * - + dbk:programlisting (dbk:programlisting) = dbk:programlisting * - + dbk:programlistingco (dbk:programlistingco) = dbk:programlistingco * - + dbk:remark (dbk:remark) = dbk:remark * - + dbk:revhistory (dbk:revhistory) = dbk:revhistory * - + dbk:screen (dbk:screen) = dbk:screen * - + dbk:screenco (dbk:screenco) = dbk:screenco * - + dbk:screenshot (dbk:screenshot) = dbk:screenshot * - + dbk:synopsis (dbk:synopsis) = dbk:synopsis * - + dbk:task (dbk:task) = dbk:task * - -[dbk:database] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines - - dbk:class (String) - -[dbk:date] > argeodbk:base, argeodbk:linkingAttributes - + jcr:xmltext (dbk:xmltext) = dbk:xmltext * - -[dbk:dedication] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled - + dbk:anchor (dbk:anchor) = dbk:anchor * - + dbk:annotation (dbk:annotation) = dbk:annotation * - + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead * - + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject * - + dbk:remark (dbk:remark) = dbk:remark * - + dbk:revhistory (dbk:revhistory) = dbk:revhistory * - + dbk:screenshot (dbk:screenshot) = dbk:screenshot * - + dbk:subtitle (dbk:subtitle) = dbk:subtitle * - - dbk:label (String) - - dbk:status (String) - -[dbk:destructorsynopsis] > argeodbk:base, argeodbk:linkingAttributes - + dbk:exceptionname (dbk:exceptionname) = dbk:exceptionname * - + dbk:methodname (dbk:methodname) = dbk:methodname - + dbk:methodparam (dbk:methodparam) = dbk:methodparam * - + dbk:modifier (dbk:modifier) = dbk:modifier * - + dbk:void (dbk:void) = dbk:void - - dbk:language (String) - -[dbk:edition] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines - -[dbk:editor] > argeodbk:base, argeodbk:linkingAttributes - + dbk:address (dbk:address) = dbk:address * - + dbk:affiliation (dbk:affiliation) = dbk:affiliation * - + dbk:contrib (dbk:contrib) = dbk:contrib * - + dbk:email (dbk:email) = dbk:email * - + dbk:orgdiv (dbk:orgdiv) = dbk:orgdiv * - + dbk:orgname (dbk:orgname) = dbk:orgname - + dbk:personblurb (dbk:personblurb) = dbk:personblurb * - + dbk:personname (dbk:personname) = dbk:personname - + dbk:uri (dbk:uri) = dbk:uri * - -[dbk:email] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines - -[dbk:emphasis] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:markupInlines, argeodbk:publishingInlines, argeodbk:techDocInlines, argeodbk:ubiquitousInlines - -[dbk:entry] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:markupInlines, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:publishingInlines, argeodbk:techDocElements, argeodbk:techDocInlines, argeodbk:ubiquitousInlines - + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead * - + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject * - + dbk:revhistory (dbk:revhistory) = dbk:revhistory * - + dbk:screenshot (dbk:screenshot) = dbk:screenshot * - - dbk:align (String) - - dbk:char (String) - - dbk:charoff (String) - - dbk:colname (String) - - dbk:colsep (String) - - dbk:morerows (String) - - dbk:nameend (String) - - dbk:namest (String) - - dbk:rotate (String) - - dbk:rowsep (String) - - dbk:spanname (String) - - dbk:valign (String) - -[dbk:entrytbl] > argeodbk:base, argeodbk:linkingAttributes - + dbk:colspec (dbk:colspec) = dbk:colspec * - + dbk:spanspec (dbk:spanspec) = dbk:spanspec * - + dbk:tbody (dbk:tbody) = dbk:tbody - + dbk:thead (dbk:thead) = dbk:thead - - dbk:align (String) - - dbk:char (String) - - dbk:charoff (String) - - dbk:colname (String) - - dbk:cols (String) - - dbk:colsep (String) - - dbk:nameend (String) - - dbk:namest (String) - - dbk:rowsep (String) - - dbk:spanname (String) - - dbk:tgroupstyle (String) - -[dbk:envar] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines - -[dbk:epigraph] > argeodbk:base, argeodbk:linkingAttributes, argeodbk:paragraphElements - + dbk:anchor (dbk:anchor) = dbk:anchor * - + dbk:attribution (dbk:attribution) = dbk:attribution - + dbk:info (dbk:info) = dbk:info - + dbk:literallayout (dbk:literallayout) = dbk:literallayout * - -[dbk:equation] > argeodbk:base, argeodbk:linkingAttributes, argeodbk:titled - + dbk:alt (dbk:alt) = dbk:alt - + dbk:caption (dbk:caption) = dbk:caption - + dbk:mathphrase (dbk:mathphrase) = dbk:mathphrase * - + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject * - - dbk:floatstyle (String) - - dbk:label (String) - - dbk:pgwide (String) - -[dbk:errorcode] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines - -[dbk:errorname] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines - -[dbk:errortext] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines - -[dbk:errortype] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines - -[dbk:example] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled - + dbk:anchor (dbk:anchor) = dbk:anchor * - + dbk:annotation (dbk:annotation) = dbk:annotation * - + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead * - + dbk:caption (dbk:caption) = dbk:caption - + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject * - + dbk:remark (dbk:remark) = dbk:remark * - + dbk:revhistory (dbk:revhistory) = dbk:revhistory * - + dbk:screenshot (dbk:screenshot) = dbk:screenshot * - - dbk:floatstyle (String) - - dbk:label (String) - - dbk:pgwide (String) - - dbk:width (String) - -[dbk:exceptionname] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines - -[dbk:extendedlink] > argeodbk:base - + dbk:arc (dbk:arc) = dbk:arc * - + dbk:locator (dbk:locator) = dbk:locator * - -[dbk:fax] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines - -[dbk:fieldsynopsis] > argeodbk:base, argeodbk:linkingAttributes - + dbk:initializer (dbk:initializer) = dbk:initializer - + dbk:modifier (dbk:modifier) = dbk:modifier * - + dbk:type (dbk:type) = dbk:type - + dbk:varname (dbk:varname) = dbk:varname - - dbk:language (String) - -[dbk:figure] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled - + dbk:anchor (dbk:anchor) = dbk:anchor * - + dbk:annotation (dbk:annotation) = dbk:annotation * - + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead * - + dbk:caption (dbk:caption) = dbk:caption - + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject * - + dbk:remark (dbk:remark) = dbk:remark * - + dbk:revhistory (dbk:revhistory) = dbk:revhistory * - + dbk:screenshot (dbk:screenshot) = dbk:screenshot * - - dbk:floatstyle (String) - - dbk:label (String) - - dbk:pgwide (String) - -[dbk:filename] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines - - dbk:class (String) - - dbk:path (String) - -[dbk:firstname] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines - -[dbk:firstterm] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:markupInlines, argeodbk:publishingInlines, argeodbk:techDocInlines, argeodbk:ubiquitousInlines - - dbk:baseform (String) - -[dbk:footnote] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements - + dbk:anchor (dbk:anchor) = dbk:anchor * - + dbk:annotation (dbk:annotation) = dbk:annotation * - + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead * - + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject * - + dbk:remark (dbk:remark) = dbk:remark * - + dbk:revhistory (dbk:revhistory) = dbk:revhistory * - + dbk:screenshot (dbk:screenshot) = dbk:screenshot * - - dbk:label (String) - -[dbk:footnoteref] > argeodbk:base, argeodbk:linkingAttributes - - dbk:label (String) - -[dbk:foreignphrase] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:publishingInlines - + dbk:anchor (dbk:anchor) = dbk:anchor * - + dbk:application (dbk:application) = dbk:application * - + dbk:biblioref (dbk:biblioref) = dbk:biblioref * - + dbk:database (dbk:database) = dbk:database * - + dbk:hardware (dbk:hardware) = dbk:hardware * - + dbk:inlinemediaobject (dbk:inlinemediaobject) = dbk:inlinemediaobject * - + dbk:link (dbk:link) = dbk:link * - + dbk:olink (dbk:olink) = dbk:olink * - + dbk:phrase (dbk:phrase) = dbk:phrase * - + dbk:productname (dbk:productname) = dbk:productname * - + dbk:productnumber (dbk:productnumber) = dbk:productnumber * - + dbk:subscript (dbk:subscript) = dbk:subscript * - + dbk:superscript (dbk:superscript) = dbk:superscript * - + dbk:trademark (dbk:trademark) = dbk:trademark * - + dbk:xref (dbk:xref) = dbk:xref * - + jcr:xmltext (dbk:xmltext) = dbk:xmltext * - -[dbk:formalpara] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:titled - + dbk:para (dbk:para) = dbk:para - -[dbk:funcdef] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines - + dbk:function (dbk:function) = dbk:function * - + dbk:type (dbk:type) = dbk:type * - -[dbk:funcparams] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines - -[dbk:funcprototype] > argeodbk:base, argeodbk:linkingAttributes - + dbk:funcdef (dbk:funcdef) = dbk:funcdef - + dbk:modifier (dbk:modifier) = dbk:modifier * - + dbk:paramdef (dbk:paramdef) = dbk:paramdef * - + dbk:varargs (dbk:varargs) = dbk:varargs - + dbk:varargs (dbk:varargs) = dbk:varargs - + dbk:void (dbk:void) = dbk:void - -[dbk:funcsynopsis] > argeodbk:base, argeodbk:linkingAttributes - + dbk:funcprototype (dbk:funcprototype) = dbk:funcprototype * - + dbk:funcsynopsisinfo (dbk:funcsynopsisinfo) = dbk:funcsynopsisinfo * - + dbk:info (dbk:info) = dbk:info - - dbk:language (String) - -[dbk:funcsynopsisinfo] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:markupInlines, argeodbk:publishingInlines, argeodbk:techDocInlines, argeodbk:ubiquitousInlines - + dbk:co (dbk:co) = dbk:co * - + dbk:info (dbk:info) = dbk:info * - + dbk:lineannotation (dbk:lineannotation) = dbk:lineannotation * - + dbk:textobject (dbk:textobject) = dbk:textobject * - - dbk:continuation (String) - - dbk:language (String) - - dbk:linenumbering (String) - - dbk:startinglinenumber (String) - - xml:space (String) - -[dbk:function] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines - -[dbk:glossary] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled - + dbk:anchor (dbk:anchor) = dbk:anchor * - + dbk:annotation (dbk:annotation) = dbk:annotation * - + dbk:bibliography (dbk:bibliography) = dbk:bibliography - + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead * - + dbk:glossdiv (dbk:glossdiv) = dbk:glossdiv * - + dbk:glossentry (dbk:glossentry) = dbk:glossentry * - + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject * - + dbk:remark (dbk:remark) = dbk:remark * - + dbk:revhistory (dbk:revhistory) = dbk:revhistory * - + dbk:screenshot (dbk:screenshot) = dbk:screenshot * - + dbk:subtitle (dbk:subtitle) = dbk:subtitle * - - dbk:label (String) - - dbk:status (String) - -[dbk:glossdef] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements - + dbk:anchor (dbk:anchor) = dbk:anchor * - + dbk:annotation (dbk:annotation) = dbk:annotation * - + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead * - + dbk:glossseealso (dbk:glossseealso) = dbk:glossseealso * - + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject * - + dbk:remark (dbk:remark) = dbk:remark * - + dbk:revhistory (dbk:revhistory) = dbk:revhistory * - + dbk:screenshot (dbk:screenshot) = dbk:screenshot * - - dbk:subject (String) - -[dbk:glossdiv] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled - + dbk:anchor (dbk:anchor) = dbk:anchor * - + dbk:annotation (dbk:annotation) = dbk:annotation * - + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead * - + dbk:glossentry (dbk:glossentry) = dbk:glossentry * - + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject * - + dbk:remark (dbk:remark) = dbk:remark * - + dbk:revhistory (dbk:revhistory) = dbk:revhistory * - + dbk:screenshot (dbk:screenshot) = dbk:screenshot * - + dbk:subtitle (dbk:subtitle) = dbk:subtitle * - - dbk:label (String) - - dbk:status (String) - -[dbk:glossentry] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes - + dbk:abbrev (dbk:abbrev) = dbk:abbrev - + dbk:acronym (dbk:acronym) = dbk:acronym - + dbk:glossdef (dbk:glossdef) = dbk:glossdef * - + dbk:glosssee (dbk:glosssee) = dbk:glosssee - + dbk:glossterm (dbk:glossterm) = dbk:glossterm - - dbk:sortas (String) - -[dbk:glosslist] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled - + dbk:anchor (dbk:anchor) = dbk:anchor * - + dbk:annotation (dbk:annotation) = dbk:annotation * - + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead * - + dbk:glossentry (dbk:glossentry) = dbk:glossentry * - + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject * - + dbk:remark (dbk:remark) = dbk:remark * - + dbk:revhistory (dbk:revhistory) = dbk:revhistory * - + dbk:screenshot (dbk:screenshot) = dbk:screenshot * - -[dbk:glosssee] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:markupInlines, argeodbk:publishingInlines, argeodbk:techDocInlines, argeodbk:ubiquitousInlines - - dbk:otherterm (Reference) - -[dbk:glossseealso] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:markupInlines, argeodbk:publishingInlines, argeodbk:techDocInlines, argeodbk:ubiquitousInlines - - dbk:otherterm (Reference) - -[dbk:glossterm] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:markupInlines, argeodbk:publishingInlines, argeodbk:techDocInlines, argeodbk:ubiquitousInlines - - dbk:baseform (String) - -[dbk:group] > argeodbk:base, argeodbk:linkingAttributes - + dbk:arg (dbk:arg) = dbk:arg * - + dbk:group (dbk:group) = dbk:group * - + dbk:option (dbk:option) = dbk:option * - + dbk:replaceable (dbk:replaceable) = dbk:replaceable * - + dbk:sbr (dbk:sbr) = dbk:sbr * - + dbk:synopfragmentref (dbk:synopfragmentref) = dbk:synopfragmentref * - - dbk:choice (String) - - dbk:rep (String) - -[dbk:guibutton] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines - + dbk:accel (dbk:accel) = dbk:accel * - -[dbk:guiicon] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines - + dbk:accel (dbk:accel) = dbk:accel * - -[dbk:guilabel] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines - + dbk:accel (dbk:accel) = dbk:accel * - -[dbk:guimenu] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines - + dbk:accel (dbk:accel) = dbk:accel * - -[dbk:guimenuitem] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines - + dbk:accel (dbk:accel) = dbk:accel * - -[dbk:guisubmenu] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines - + dbk:accel (dbk:accel) = dbk:accel * - -[dbk:hardware] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines - -[dbk:holder] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines - -[dbk:honorific] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines - -[dbk:imagedata] > argeodbk:base - + dbk:info (dbk:info) = dbk:info - - dbk:align (String) - - dbk:contentdepth (String) - - dbk:contentwidth (String) - - dbk:depth (String) - - dbk:entityref (String) - - dbk:fileref (String) - - dbk:format (String) - - dbk:scale (String) - - dbk:scalefit (String) - - dbk:valign (String) - - dbk:width (String) - -[dbk:imageobject] > argeodbk:base, argeodbk:linkingAttributes - + dbk:imagedata (dbk:imagedata) = dbk:imagedata - + dbk:info (dbk:info) = dbk:info - -[dbk:imageobjectco] > argeodbk:base, argeodbk:linkingAttributes - + dbk:areaspec (dbk:areaspec) = dbk:areaspec - + dbk:calloutlist (dbk:calloutlist) = dbk:calloutlist * - + dbk:imageobject (dbk:imageobject) = dbk:imageobject * - + dbk:info (dbk:info) = dbk:info - -[dbk:important] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled - + dbk:anchor (dbk:anchor) = dbk:anchor * - + dbk:annotation (dbk:annotation) = dbk:annotation * - + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead * - + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject * - + dbk:remark (dbk:remark) = dbk:remark * - + dbk:revhistory (dbk:revhistory) = dbk:revhistory * - + dbk:screenshot (dbk:screenshot) = dbk:screenshot * - -[dbk:index] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled - + dbk:anchor (dbk:anchor) = dbk:anchor * - + dbk:annotation (dbk:annotation) = dbk:annotation * - + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead * - + dbk:indexdiv (dbk:indexdiv) = dbk:indexdiv * - + dbk:indexentry (dbk:indexentry) = dbk:indexentry * - + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject * - + dbk:remark (dbk:remark) = dbk:remark * - + dbk:revhistory (dbk:revhistory) = dbk:revhistory * - + dbk:screenshot (dbk:screenshot) = dbk:screenshot * - + dbk:subtitle (dbk:subtitle) = dbk:subtitle * - - dbk:label (String) - - dbk:status (String) - - dbk:type (String) - -[dbk:indexdiv] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled - + dbk:anchor (dbk:anchor) = dbk:anchor * - + dbk:annotation (dbk:annotation) = dbk:annotation * - + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead * - + dbk:indexentry (dbk:indexentry) = dbk:indexentry * - + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject * - + dbk:remark (dbk:remark) = dbk:remark * - + dbk:revhistory (dbk:revhistory) = dbk:revhistory * - + dbk:screenshot (dbk:screenshot) = dbk:screenshot * - + dbk:subtitle (dbk:subtitle) = dbk:subtitle * - - dbk:label (String) - - dbk:status (String) - -[dbk:indexentry] > argeodbk:base, argeodbk:linkingAttributes - + dbk:primaryie (dbk:primaryie) = dbk:primaryie - + dbk:secondaryie (dbk:secondaryie) = dbk:secondaryie * - + dbk:seealsoie (dbk:seealsoie) = dbk:seealsoie * - + dbk:seeie (dbk:seeie) = dbk:seeie * - + dbk:tertiaryie (dbk:tertiaryie) = dbk:tertiaryie * - -[dbk:indexterm] > argeodbk:base, argeodbk:linkingAttributes - + dbk:primary (dbk:primary) = dbk:primary - + dbk:secondary (dbk:secondary) = dbk:secondary - + dbk:see (dbk:see) = dbk:see - + dbk:seealso (dbk:seealso) = dbk:seealso * - + dbk:tertiary (dbk:tertiary) = dbk:tertiary - - dbk:class (String) - - dbk:pagenum (String) - - dbk:scope (String) - - dbk:significance (String) - - dbk:startref (Reference) - - dbk:type (String) - - dbk:zone (String) - -[dbk:info] > argeodbk:base - + dbk:abstract (dbk:abstract) = dbk:abstract * - + dbk:address (dbk:address) = dbk:address * - + dbk:annotation (dbk:annotation) = dbk:annotation * - + dbk:artpagenums (dbk:artpagenums) = dbk:artpagenums * - + dbk:author (dbk:author) = dbk:author * - + dbk:authorgroup (dbk:authorgroup) = dbk:authorgroup * - + dbk:authorinitials (dbk:authorinitials) = dbk:authorinitials * - + dbk:bibliocoverage (dbk:bibliocoverage) = dbk:bibliocoverage * - + dbk:biblioid (dbk:biblioid) = dbk:biblioid * - + dbk:bibliomisc (dbk:bibliomisc) = dbk:bibliomisc * - + dbk:bibliomset (dbk:bibliomset) = dbk:bibliomset * - + dbk:bibliorelation (dbk:bibliorelation) = dbk:bibliorelation * - + dbk:biblioset (dbk:biblioset) = dbk:biblioset * - + dbk:bibliosource (dbk:bibliosource) = dbk:bibliosource * - + dbk:collab (dbk:collab) = dbk:collab * - + dbk:confgroup (dbk:confgroup) = dbk:confgroup * - + dbk:contractnum (dbk:contractnum) = dbk:contractnum * - + dbk:contractsponsor (dbk:contractsponsor) = dbk:contractsponsor * - + dbk:copyright (dbk:copyright) = dbk:copyright * - + dbk:cover (dbk:cover) = dbk:cover * - + dbk:date (dbk:date) = dbk:date * - + dbk:edition (dbk:edition) = dbk:edition * - + dbk:editor (dbk:editor) = dbk:editor * - + dbk:extendedlink (dbk:extendedlink) = dbk:extendedlink * - + dbk:issuenum (dbk:issuenum) = dbk:issuenum * - + dbk:itermset (dbk:itermset) = dbk:itermset * - + dbk:keywordset (dbk:keywordset) = dbk:keywordset * - + dbk:legalnotice (dbk:legalnotice) = dbk:legalnotice * - + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject * - + dbk:org (dbk:org) = dbk:org * - + dbk:orgname (dbk:orgname) = dbk:orgname * - + dbk:othercredit (dbk:othercredit) = dbk:othercredit * - + dbk:pagenums (dbk:pagenums) = dbk:pagenums * - + dbk:printhistory (dbk:printhistory) = dbk:printhistory * - + dbk:productname (dbk:productname) = dbk:productname * - + dbk:productnumber (dbk:productnumber) = dbk:productnumber * - + dbk:pubdate (dbk:pubdate) = dbk:pubdate * - + dbk:publisher (dbk:publisher) = dbk:publisher * - + dbk:publishername (dbk:publishername) = dbk:publishername * - + dbk:releaseinfo (dbk:releaseinfo) = dbk:releaseinfo * - + dbk:revhistory (dbk:revhistory) = dbk:revhistory * - + dbk:seriesvolnums (dbk:seriesvolnums) = dbk:seriesvolnums * - + dbk:subjectset (dbk:subjectset) = dbk:subjectset * - + dbk:subtitle (dbk:subtitle) = dbk:subtitle * - + dbk:title (dbk:title) = dbk:title * - + dbk:titleabbrev (dbk:titleabbrev) = dbk:titleabbrev * - + dbk:volumenum (dbk:volumenum) = dbk:volumenum * - -[dbk:informalequation] > argeodbk:base, argeodbk:linkingAttributes - + dbk:alt (dbk:alt) = dbk:alt - + dbk:caption (dbk:caption) = dbk:caption - + dbk:info (dbk:info) = dbk:info - + dbk:mathphrase (dbk:mathphrase) = dbk:mathphrase * - + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject * - -[dbk:informalexample] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements - + dbk:anchor (dbk:anchor) = dbk:anchor * - + dbk:annotation (dbk:annotation) = dbk:annotation * - + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead * - + dbk:caption (dbk:caption) = dbk:caption - + dbk:info (dbk:info) = dbk:info - + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject * - + dbk:remark (dbk:remark) = dbk:remark * - + dbk:revhistory (dbk:revhistory) = dbk:revhistory * - + dbk:screenshot (dbk:screenshot) = dbk:screenshot * - - dbk:floatstyle (String) - - dbk:width (String) - -[dbk:informalfigure] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements - + dbk:anchor (dbk:anchor) = dbk:anchor * - + dbk:annotation (dbk:annotation) = dbk:annotation * - + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead * - + dbk:caption (dbk:caption) = dbk:caption - + dbk:info (dbk:info) = dbk:info - + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject * - + dbk:remark (dbk:remark) = dbk:remark * - + dbk:revhistory (dbk:revhistory) = dbk:revhistory * - + dbk:screenshot (dbk:screenshot) = dbk:screenshot * - - dbk:floatstyle (String) - - dbk:label (String) - - dbk:pgwide (String) - -[dbk:informaltable] > argeodbk:base, argeodbk:linkingAttributes - + dbk:col (dbk:col) = dbk:col * - + dbk:colgroup (dbk:colgroup) = dbk:colgroup * - + dbk:info (dbk:info) = dbk:info - + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject * - + dbk:tbody (dbk:tbody) = dbk:tbody * - + dbk:textobject (dbk:textobject) = dbk:textobject * - + dbk:tfoot (dbk:tfoot) = dbk:tfoot - + dbk:tgroup (dbk:tgroup) = dbk:tgroup * - + dbk:thead (dbk:thead) = dbk:thead - + dbk:tr (dbk:tr) = dbk:tr * - - dbk:border (String) - - dbk:cellpadding (String) - - dbk:cellspacing (String) - - dbk:class (String) - - dbk:colsep (String) - - dbk:floatstyle (String) - - dbk:frame (String) - - dbk:lang (String) - - dbk:onclick (String) - - dbk:ondblclick (String) - - dbk:onkeydown (String) - - dbk:onkeypress (String) - - dbk:onkeyup (String) - - dbk:onmousedown (String) - - dbk:onmousemove (String) - - dbk:onmouseout (String) - - dbk:onmouseover (String) - - dbk:onmouseup (String) - - dbk:orient (String) - - dbk:pgwide (String) - - dbk:rowheader (String) - - dbk:rowsep (String) - - dbk:rules (String) - - dbk:style (String) - - dbk:summary (String) - - dbk:tabstyle (String) - - dbk:title (String) - - dbk:width (String) - -[dbk:initializer] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines - -[dbk:inlineequation] > argeodbk:base, argeodbk:linkingAttributes - + dbk:alt (dbk:alt) = dbk:alt - + dbk:inlinemediaobject (dbk:inlinemediaobject) = dbk:inlinemediaobject * - + dbk:mathphrase (dbk:mathphrase) = dbk:mathphrase * - -[dbk:inlinemediaobject] > argeodbk:base, argeodbk:linkingAttributes - + dbk:alt (dbk:alt) = dbk:alt - + dbk:audioobject (dbk:audioobject) = dbk:audioobject * - + dbk:imageobject (dbk:imageobject) = dbk:imageobject * - + dbk:imageobjectco (dbk:imageobjectco) = dbk:imageobjectco * - + dbk:info (dbk:info) = dbk:info - + dbk:textobject (dbk:textobject) = dbk:textobject * - + dbk:videoobject (dbk:videoobject) = dbk:videoobject * - -[dbk:interfacename] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines - -[dbk:issuenum] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines - -[dbk:itemizedlist] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled - + dbk:anchor (dbk:anchor) = dbk:anchor * - + dbk:annotation (dbk:annotation) = dbk:annotation * - + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead * - + dbk:listitem (dbk:listitem) = dbk:listitem * - + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject * - + dbk:remark (dbk:remark) = dbk:remark * - + dbk:revhistory (dbk:revhistory) = dbk:revhistory * - + dbk:screenshot (dbk:screenshot) = dbk:screenshot * - - dbk:mark (String) - - dbk:spacing (String) - -[dbk:itermset] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes - -[dbk:jobtitle] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines - -[dbk:keycap] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines - - dbk:function (String) - - dbk:otherfunction (String) - -[dbk:keycode] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines - -[dbk:keycombo] > argeodbk:base, argeodbk:linkingAttributes - + dbk:keycap (dbk:keycap) = dbk:keycap * - + dbk:keycombo (dbk:keycombo) = dbk:keycombo * - + dbk:keysym (dbk:keysym) = dbk:keysym * - + dbk:mousebutton (dbk:mousebutton) = dbk:mousebutton * - - dbk:action (String) - - dbk:otheraction (String) - -[dbk:keysym] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines - -[dbk:keyword] > argeodbk:base, argeodbk:linkingAttributes - + jcr:xmltext (dbk:xmltext) = dbk:xmltext * - -[dbk:keywordset] > argeodbk:base, argeodbk:linkingAttributes - + dbk:keyword (dbk:keyword) = dbk:keyword * - -[dbk:label] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines - -[dbk:legalnotice] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled - + dbk:anchor (dbk:anchor) = dbk:anchor * - + dbk:annotation (dbk:annotation) = dbk:annotation * - + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead * - + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject * - + dbk:remark (dbk:remark) = dbk:remark * - + dbk:revhistory (dbk:revhistory) = dbk:revhistory * - + dbk:screenshot (dbk:screenshot) = dbk:screenshot * - -[dbk:lhs] > argeodbk:base, argeodbk:linkingAttributes - + jcr:xmltext (dbk:xmltext) = dbk:xmltext * - -[dbk:lineage] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines - -[dbk:lineannotation] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines - -[dbk:link] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:markupInlines, argeodbk:publishingInlines, argeodbk:techDocInlines, argeodbk:ubiquitousInlines - - dbk:endterm (Reference) - - dbk:xrefstyle (String) - -[dbk:listitem] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements - + dbk:anchor (dbk:anchor) = dbk:anchor * - + dbk:annotation (dbk:annotation) = dbk:annotation * - + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead * - + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject * - + dbk:remark (dbk:remark) = dbk:remark * - + dbk:revhistory (dbk:revhistory) = dbk:revhistory * - + dbk:screenshot (dbk:screenshot) = dbk:screenshot * - - dbk:override (String) - -[dbk:literal] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines - -[dbk:literallayout] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:markupInlines, argeodbk:publishingInlines, argeodbk:techDocInlines, argeodbk:ubiquitousInlines - + dbk:co (dbk:co) = dbk:co * - + dbk:info (dbk:info) = dbk:info * - + dbk:lineannotation (dbk:lineannotation) = dbk:lineannotation * - + dbk:textobject (dbk:textobject) = dbk:textobject * - - dbk:class (String) - - dbk:continuation (String) - - dbk:language (String) - - dbk:linenumbering (String) - - dbk:startinglinenumber (String) - - xml:space (String) - -[dbk:locator] > argeodbk:base - - xlink:label (String) - -[dbk:manvolnum] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines - -[dbk:markup] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines - -[dbk:mathphrase] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines - + dbk:emphasis (dbk:emphasis) = dbk:emphasis * - -[dbk:mediaobject] > argeodbk:base, argeodbk:linkingAttributes - + dbk:alt (dbk:alt) = dbk:alt - + dbk:audioobject (dbk:audioobject) = dbk:audioobject * - + dbk:caption (dbk:caption) = dbk:caption - + dbk:imageobject (dbk:imageobject) = dbk:imageobject * - + dbk:imageobjectco (dbk:imageobjectco) = dbk:imageobjectco * - + dbk:info (dbk:info) = dbk:info - + dbk:textobject (dbk:textobject) = dbk:textobject * - + dbk:videoobject (dbk:videoobject) = dbk:videoobject * - -[dbk:member] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:markupInlines, argeodbk:publishingInlines, argeodbk:techDocInlines, argeodbk:ubiquitousInlines - -[dbk:menuchoice] > argeodbk:base, argeodbk:linkingAttributes - + dbk:guibutton (dbk:guibutton) = dbk:guibutton * - + dbk:guiicon (dbk:guiicon) = dbk:guiicon * - + dbk:guilabel (dbk:guilabel) = dbk:guilabel * - + dbk:guimenu (dbk:guimenu) = dbk:guimenu * - + dbk:guimenuitem (dbk:guimenuitem) = dbk:guimenuitem * - + dbk:guisubmenu (dbk:guisubmenu) = dbk:guisubmenu * - + dbk:shortcut (dbk:shortcut) = dbk:shortcut - -[dbk:methodname] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines - -[dbk:methodparam] > argeodbk:base, argeodbk:linkingAttributes - + dbk:funcparams (dbk:funcparams) = dbk:funcparams - + dbk:initializer (dbk:initializer) = dbk:initializer - + dbk:modifier (dbk:modifier) = dbk:modifier * - + dbk:modifier (dbk:modifier) = dbk:modifier * - + dbk:parameter (dbk:parameter) = dbk:parameter - + dbk:type (dbk:type) = dbk:type * - - dbk:choice (String) - - dbk:rep (String) - -[dbk:methodsynopsis] > argeodbk:base, argeodbk:linkingAttributes - + dbk:exceptionname (dbk:exceptionname) = dbk:exceptionname * - + dbk:methodname (dbk:methodname) = dbk:methodname - + dbk:methodparam (dbk:methodparam) = dbk:methodparam * - + dbk:modifier (dbk:modifier) = dbk:modifier * - + dbk:type (dbk:type) = dbk:type - + dbk:void (dbk:void) = dbk:void - - dbk:language (String) - -[dbk:modifier] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines - - xml:space (String) - -[dbk:mousebutton] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines - -[dbk:msg] > argeodbk:base, argeodbk:linkingAttributes, argeodbk:titled - + dbk:msgmain (dbk:msgmain) = dbk:msgmain - + dbk:msgrel (dbk:msgrel) = dbk:msgrel * - + dbk:msgsub (dbk:msgsub) = dbk:msgsub * - -[dbk:msgaud] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines - -[dbk:msgentry] > argeodbk:base, argeodbk:linkingAttributes - + dbk:msg (dbk:msg) = dbk:msg * - + dbk:msgexplan (dbk:msgexplan) = dbk:msgexplan * - + dbk:msginfo (dbk:msginfo) = dbk:msginfo - -[dbk:msgexplan] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled - + dbk:anchor (dbk:anchor) = dbk:anchor * - + dbk:annotation (dbk:annotation) = dbk:annotation * - + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead * - + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject * - + dbk:remark (dbk:remark) = dbk:remark * - + dbk:revhistory (dbk:revhistory) = dbk:revhistory * - + dbk:screenshot (dbk:screenshot) = dbk:screenshot * - -[dbk:msginfo] > argeodbk:base, argeodbk:linkingAttributes - + dbk:msgaud (dbk:msgaud) = dbk:msgaud * - + dbk:msglevel (dbk:msglevel) = dbk:msglevel * - + dbk:msgorig (dbk:msgorig) = dbk:msgorig * - -[dbk:msglevel] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines - -[dbk:msgmain] > argeodbk:base, argeodbk:linkingAttributes, argeodbk:titled - + dbk:msgtext (dbk:msgtext) = dbk:msgtext - -[dbk:msgorig] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines - -[dbk:msgrel] > argeodbk:base, argeodbk:linkingAttributes, argeodbk:titled - + dbk:msgtext (dbk:msgtext) = dbk:msgtext - -[dbk:msgset] > argeodbk:base, argeodbk:linkingAttributes, argeodbk:titled - + dbk:msgentry (dbk:msgentry) = dbk:msgentry * - + dbk:simplemsgentry (dbk:simplemsgentry) = dbk:simplemsgentry * - -[dbk:msgsub] > argeodbk:base, argeodbk:linkingAttributes, argeodbk:titled - + dbk:msgtext (dbk:msgtext) = dbk:msgtext - -[dbk:msgtext] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements - + dbk:anchor (dbk:anchor) = dbk:anchor * - + dbk:annotation (dbk:annotation) = dbk:annotation * - + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead * - + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject * - + dbk:remark (dbk:remark) = dbk:remark * - + dbk:revhistory (dbk:revhistory) = dbk:revhistory * - + dbk:screenshot (dbk:screenshot) = dbk:screenshot * - -[dbk:nonterminal] > argeodbk:base, argeodbk:linkingAttributes - + jcr:xmltext (dbk:xmltext) = dbk:xmltext * - - dbk:def (String) - -[dbk:note] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled - + dbk:anchor (dbk:anchor) = dbk:anchor * - + dbk:annotation (dbk:annotation) = dbk:annotation * - + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead * - + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject * - + dbk:remark (dbk:remark) = dbk:remark * - + dbk:revhistory (dbk:revhistory) = dbk:revhistory * - + dbk:screenshot (dbk:screenshot) = dbk:screenshot * - -[dbk:olink] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:markupInlines, argeodbk:publishingInlines, argeodbk:techDocInlines, argeodbk:ubiquitousInlines - - dbk:localinfo (String) - - dbk:targetdoc (String) - - dbk:targetptr (String) - - dbk:type (String) - - dbk:xrefstyle (String) - -[dbk:ooclass] > argeodbk:base, argeodbk:linkingAttributes - + dbk:classname (dbk:classname) = dbk:classname - + dbk:modifier (dbk:modifier) = dbk:modifier * - + dbk:package (dbk:package) = dbk:package * - -[dbk:ooexception] > argeodbk:base, argeodbk:linkingAttributes - + dbk:exceptionname (dbk:exceptionname) = dbk:exceptionname - + dbk:modifier (dbk:modifier) = dbk:modifier * - + dbk:package (dbk:package) = dbk:package * - -[dbk:oointerface] > argeodbk:base, argeodbk:linkingAttributes - + dbk:interfacename (dbk:interfacename) = dbk:interfacename - + dbk:modifier (dbk:modifier) = dbk:modifier * - + dbk:package (dbk:package) = dbk:package * - -[dbk:option] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines - -[dbk:optional] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines - -[dbk:orderedlist] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled - + dbk:anchor (dbk:anchor) = dbk:anchor * - + dbk:annotation (dbk:annotation) = dbk:annotation * - + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead * - + dbk:listitem (dbk:listitem) = dbk:listitem * - + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject * - + dbk:remark (dbk:remark) = dbk:remark * - + dbk:revhistory (dbk:revhistory) = dbk:revhistory * - + dbk:screenshot (dbk:screenshot) = dbk:screenshot * - - dbk:continuation (String) - - dbk:inheritnum (String) - - dbk:numeration (String) - - dbk:spacing (String) - - dbk:startingnumber (String) - -[dbk:org] > argeodbk:base, argeodbk:linkingAttributes - + dbk:address (dbk:address) = dbk:address * - + dbk:affiliation (dbk:affiliation) = dbk:affiliation * - + dbk:email (dbk:email) = dbk:email * - + dbk:orgdiv (dbk:orgdiv) = dbk:orgdiv * - + dbk:orgname (dbk:orgname) = dbk:orgname - + dbk:uri (dbk:uri) = dbk:uri * - -[dbk:orgdiv] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:markupInlines, argeodbk:publishingInlines, argeodbk:techDocInlines, argeodbk:ubiquitousInlines - -[dbk:orgname] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines - - dbk:class (String) - - dbk:otherclass (String) - -[dbk:otheraddr] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines - -[dbk:othercredit] > argeodbk:base, argeodbk:linkingAttributes - + dbk:address (dbk:address) = dbk:address * - + dbk:affiliation (dbk:affiliation) = dbk:affiliation * - + dbk:contrib (dbk:contrib) = dbk:contrib * - + dbk:email (dbk:email) = dbk:email * - + dbk:orgdiv (dbk:orgdiv) = dbk:orgdiv * - + dbk:orgname (dbk:orgname) = dbk:orgname - + dbk:personblurb (dbk:personblurb) = dbk:personblurb * - + dbk:personname (dbk:personname) = dbk:personname - + dbk:uri (dbk:uri) = dbk:uri * - - dbk:class (String) - - dbk:otherclass (String) - -[dbk:othername] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines - -[dbk:package] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines - -[dbk:pagenums] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines - -[dbk:para] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:markupInlines, argeodbk:publishingElements, argeodbk:publishingInlines, argeodbk:techDocElements, argeodbk:techDocInlines, argeodbk:ubiquitousInlines - + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead * - + dbk:info (dbk:info) = dbk:info * - + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject * - + dbk:revhistory (dbk:revhistory) = dbk:revhistory * - + dbk:screenshot (dbk:screenshot) = dbk:screenshot * - -[dbk:paramdef] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines - + dbk:funcparams (dbk:funcparams) = dbk:funcparams * - + dbk:initializer (dbk:initializer) = dbk:initializer * - + dbk:parameter (dbk:parameter) = dbk:parameter * - + dbk:type (dbk:type) = dbk:type * - - dbk:choice (String) - -[dbk:parameter] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines - - dbk:class (String) - -[dbk:part] > argeodbk:base, argeodbk:linkingAttributes, argeodbk:titled - + dbk:acknowledgements (dbk:acknowledgements) = dbk:acknowledgements * - + dbk:appendix (dbk:appendix) = dbk:appendix * - + dbk:article (dbk:article) = dbk:article * - + dbk:bibliography (dbk:bibliography) = dbk:bibliography * - + dbk:chapter (dbk:chapter) = dbk:chapter * - + dbk:colophon (dbk:colophon) = dbk:colophon * - + dbk:dedication (dbk:dedication) = dbk:dedication * - + dbk:glossary (dbk:glossary) = dbk:glossary * - + dbk:index (dbk:index) = dbk:index * - + dbk:partintro (dbk:partintro) = dbk:partintro - + dbk:preface (dbk:preface) = dbk:preface * - + dbk:refentry (dbk:refentry) = dbk:refentry * - + dbk:reference (dbk:reference) = dbk:reference * - + dbk:subtitle (dbk:subtitle) = dbk:subtitle * - + dbk:toc (dbk:toc) = dbk:toc * - - dbk:label (String) - - dbk:status (String) - -[dbk:partintro] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled - + dbk:anchor (dbk:anchor) = dbk:anchor * - + dbk:annotation (dbk:annotation) = dbk:annotation * - + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead * - + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject * - + dbk:refentry (dbk:refentry) = dbk:refentry * - + dbk:remark (dbk:remark) = dbk:remark * - + dbk:revhistory (dbk:revhistory) = dbk:revhistory * - + dbk:screenshot (dbk:screenshot) = dbk:screenshot * - + dbk:sect1 (dbk:sect1) = dbk:sect1 * - + dbk:section (dbk:section) = dbk:section * - + dbk:simplesect (dbk:simplesect) = dbk:simplesect * - + dbk:simplesect (dbk:simplesect) = dbk:simplesect * - + dbk:subtitle (dbk:subtitle) = dbk:subtitle * - - dbk:label (String) - - dbk:status (String) - -[dbk:person] > argeodbk:base, argeodbk:linkingAttributes - + dbk:address (dbk:address) = dbk:address * - + dbk:affiliation (dbk:affiliation) = dbk:affiliation * - + dbk:email (dbk:email) = dbk:email * - + dbk:personblurb (dbk:personblurb) = dbk:personblurb * - + dbk:personname (dbk:personname) = dbk:personname - + dbk:uri (dbk:uri) = dbk:uri * - -[dbk:personblurb] > argeodbk:base, argeodbk:linkingAttributes, argeodbk:paragraphElements, argeodbk:titled - + dbk:anchor (dbk:anchor) = dbk:anchor * - -[dbk:personname] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines - + dbk:firstname (dbk:firstname) = dbk:firstname * - + dbk:honorific (dbk:honorific) = dbk:honorific * - + dbk:lineage (dbk:lineage) = dbk:lineage * - + dbk:othername (dbk:othername) = dbk:othername * - + dbk:surname (dbk:surname) = dbk:surname * - -[dbk:phone] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines - -[dbk:phrase] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:markupInlines, argeodbk:publishingInlines, argeodbk:techDocInlines, argeodbk:ubiquitousInlines - -[dbk:pob] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines - -[dbk:postcode] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines - -[dbk:preface] > argeodbk:abstractSection, argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled - + dbk:anchor (dbk:anchor) = dbk:anchor * - + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject * - + dbk:refentry (dbk:refentry) = dbk:refentry * - + dbk:screenshot (dbk:screenshot) = dbk:screenshot * - + dbk:sect1 (dbk:sect1) = dbk:sect1 * - + dbk:section (dbk:section) = dbk:section * - + dbk:simplesect (dbk:simplesect) = dbk:simplesect * - + dbk:simplesect (dbk:simplesect) = dbk:simplesect * - -[dbk:primary] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:markupInlines, argeodbk:publishingInlines, argeodbk:techDocInlines, argeodbk:ubiquitousInlines - - dbk:sortas (String) - -[dbk:primaryie] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:markupInlines, argeodbk:publishingInlines, argeodbk:techDocInlines, argeodbk:ubiquitousInlines - - dbk:linkends (String) - -[dbk:printhistory] > argeodbk:base, argeodbk:linkingAttributes, argeodbk:paragraphElements - + dbk:anchor (dbk:anchor) = dbk:anchor * - -[dbk:procedure] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled - + dbk:anchor (dbk:anchor) = dbk:anchor * - + dbk:annotation (dbk:annotation) = dbk:annotation * - + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead * - + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject * - + dbk:remark (dbk:remark) = dbk:remark * - + dbk:revhistory (dbk:revhistory) = dbk:revhistory * - + dbk:screenshot (dbk:screenshot) = dbk:screenshot * - + dbk:step (dbk:step) = dbk:step * - -[dbk:production] > argeodbk:base, argeodbk:linkingAttributes - + dbk:constraint (dbk:constraint) = dbk:constraint * - + dbk:lhs (dbk:lhs) = dbk:lhs - + dbk:rhs (dbk:rhs) = dbk:rhs - -[dbk:productionrecap] > argeodbk:base, argeodbk:linkingAttributes - -[dbk:productionset] > argeodbk:base, argeodbk:linkingAttributes, argeodbk:titled - + dbk:production (dbk:production) = dbk:production * - + dbk:productionrecap (dbk:productionrecap) = dbk:productionrecap * - -[dbk:productname] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines - - dbk:class (String) - -[dbk:productnumber] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines - -[dbk:programlisting] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:markupInlines, argeodbk:publishingInlines, argeodbk:techDocInlines, argeodbk:ubiquitousInlines - + dbk:co (dbk:co) = dbk:co * - + dbk:info (dbk:info) = dbk:info * - + dbk:lineannotation (dbk:lineannotation) = dbk:lineannotation * - + dbk:textobject (dbk:textobject) = dbk:textobject * - - dbk:continuation (String) - - dbk:language (String) - - dbk:linenumbering (String) - - dbk:startinglinenumber (String) - - dbk:width (String) - - xml:space (String) - -[dbk:programlistingco] > argeodbk:base, argeodbk:linkingAttributes - + dbk:areaspec (dbk:areaspec) = dbk:areaspec - + dbk:calloutlist (dbk:calloutlist) = dbk:calloutlist * - + dbk:info (dbk:info) = dbk:info - + dbk:programlisting (dbk:programlisting) = dbk:programlisting - -[dbk:prompt] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines - + dbk:co (dbk:co) = dbk:co * - -[dbk:property] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines - -[dbk:pubdate] > argeodbk:base, argeodbk:linkingAttributes - + jcr:xmltext (dbk:xmltext) = dbk:xmltext * - -[dbk:publisher] > argeodbk:base, argeodbk:linkingAttributes - + dbk:address (dbk:address) = dbk:address * - + dbk:publishername (dbk:publishername) = dbk:publishername - -[dbk:publishername] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines - -[dbk:qandadiv] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled - + dbk:anchor (dbk:anchor) = dbk:anchor * - + dbk:annotation (dbk:annotation) = dbk:annotation * - + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead * - + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject * - + dbk:qandadiv (dbk:qandadiv) = dbk:qandadiv * - + dbk:qandaentry (dbk:qandaentry) = dbk:qandaentry * - + dbk:remark (dbk:remark) = dbk:remark * - + dbk:revhistory (dbk:revhistory) = dbk:revhistory * - + dbk:screenshot (dbk:screenshot) = dbk:screenshot * - -[dbk:qandaentry] > argeodbk:base, argeodbk:linkingAttributes, argeodbk:titled - + dbk:answer (dbk:answer) = dbk:answer * - + dbk:question (dbk:question) = dbk:question - -[dbk:qandaset] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled - + dbk:anchor (dbk:anchor) = dbk:anchor * - + dbk:annotation (dbk:annotation) = dbk:annotation * - + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead * - + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject * - + dbk:qandadiv (dbk:qandadiv) = dbk:qandadiv * - + dbk:qandaentry (dbk:qandaentry) = dbk:qandaentry * - + dbk:remark (dbk:remark) = dbk:remark * - + dbk:revhistory (dbk:revhistory) = dbk:revhistory * - + dbk:screenshot (dbk:screenshot) = dbk:screenshot * - - dbk:defaultlabel (String) - -[dbk:question] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements - + dbk:anchor (dbk:anchor) = dbk:anchor * - + dbk:annotation (dbk:annotation) = dbk:annotation * - + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead * - + dbk:label (dbk:label) = dbk:label - + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject * - + dbk:remark (dbk:remark) = dbk:remark * - + dbk:revhistory (dbk:revhistory) = dbk:revhistory * - + dbk:screenshot (dbk:screenshot) = dbk:screenshot * - -[dbk:quote] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:markupInlines, argeodbk:publishingInlines, argeodbk:techDocInlines, argeodbk:ubiquitousInlines - -[dbk:refclass] > argeodbk:base, argeodbk:linkingAttributes - + dbk:application (dbk:application) = dbk:application * - + jcr:xmltext (dbk:xmltext) = dbk:xmltext * - -[dbk:refdescriptor] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:markupInlines, argeodbk:publishingInlines, argeodbk:techDocInlines, argeodbk:ubiquitousInlines - -[dbk:refentry] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes - + dbk:info (dbk:info) = dbk:info - + dbk:refmeta (dbk:refmeta) = dbk:refmeta - + dbk:refnamediv (dbk:refnamediv) = dbk:refnamediv * - + dbk:refsect1 (dbk:refsect1) = dbk:refsect1 * - + dbk:refsection (dbk:refsection) = dbk:refsection * - + dbk:refsynopsisdiv (dbk:refsynopsisdiv) = dbk:refsynopsisdiv - - dbk:label (String) - - dbk:status (String) - -[dbk:refentrytitle] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:markupInlines, argeodbk:publishingInlines, argeodbk:techDocInlines, argeodbk:ubiquitousInlines - -[dbk:reference] > argeodbk:base, argeodbk:linkingAttributes, argeodbk:titled - + dbk:partintro (dbk:partintro) = dbk:partintro - + dbk:refentry (dbk:refentry) = dbk:refentry * - + dbk:subtitle (dbk:subtitle) = dbk:subtitle * - - dbk:label (String) - - dbk:status (String) - -[dbk:refmeta] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes - + dbk:manvolnum (dbk:manvolnum) = dbk:manvolnum - + dbk:refentrytitle (dbk:refentrytitle) = dbk:refentrytitle - + dbk:refmiscinfo (dbk:refmiscinfo) = dbk:refmiscinfo * - -[dbk:refmiscinfo] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines - - dbk:class (String) - - dbk:otherclass (String) - -[dbk:refname] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:markupInlines, argeodbk:publishingInlines, argeodbk:techDocInlines, argeodbk:ubiquitousInlines - -[dbk:refnamediv] > argeodbk:base, argeodbk:linkingAttributes - + dbk:refclass (dbk:refclass) = dbk:refclass * - + dbk:refdescriptor (dbk:refdescriptor) = dbk:refdescriptor - + dbk:refname (dbk:refname) = dbk:refname * - + dbk:refpurpose (dbk:refpurpose) = dbk:refpurpose - -[dbk:refpurpose] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:markupInlines, argeodbk:publishingInlines, argeodbk:techDocInlines, argeodbk:ubiquitousInlines - -[dbk:refsect1] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled - + dbk:anchor (dbk:anchor) = dbk:anchor * - + dbk:annotation (dbk:annotation) = dbk:annotation * - + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead * - + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject * - + dbk:refsect2 (dbk:refsect2) = dbk:refsect2 * - + dbk:refsect2 (dbk:refsect2) = dbk:refsect2 * - + dbk:remark (dbk:remark) = dbk:remark * - + dbk:revhistory (dbk:revhistory) = dbk:revhistory * - + dbk:screenshot (dbk:screenshot) = dbk:screenshot * - + dbk:subtitle (dbk:subtitle) = dbk:subtitle * - - dbk:label (String) - - dbk:status (String) - -[dbk:refsect2] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled - + dbk:anchor (dbk:anchor) = dbk:anchor * - + dbk:annotation (dbk:annotation) = dbk:annotation * - + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead * - + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject * - + dbk:refsect3 (dbk:refsect3) = dbk:refsect3 * - + dbk:refsect3 (dbk:refsect3) = dbk:refsect3 * - + dbk:remark (dbk:remark) = dbk:remark * - + dbk:revhistory (dbk:revhistory) = dbk:revhistory * - + dbk:screenshot (dbk:screenshot) = dbk:screenshot * - + dbk:subtitle (dbk:subtitle) = dbk:subtitle * - - dbk:label (String) - - dbk:status (String) - -[dbk:refsect3] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled - + dbk:anchor (dbk:anchor) = dbk:anchor * - + dbk:annotation (dbk:annotation) = dbk:annotation * - + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead * - + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject * - + dbk:remark (dbk:remark) = dbk:remark * - + dbk:revhistory (dbk:revhistory) = dbk:revhistory * - + dbk:screenshot (dbk:screenshot) = dbk:screenshot * - + dbk:subtitle (dbk:subtitle) = dbk:subtitle * - - dbk:label (String) - - dbk:status (String) - -[dbk:refsection] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled - + dbk:anchor (dbk:anchor) = dbk:anchor * - + dbk:annotation (dbk:annotation) = dbk:annotation * - + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead * - + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject * - + dbk:refsection (dbk:refsection) = dbk:refsection * - + dbk:refsection (dbk:refsection) = dbk:refsection * - + dbk:remark (dbk:remark) = dbk:remark * - + dbk:revhistory (dbk:revhistory) = dbk:revhistory * - + dbk:screenshot (dbk:screenshot) = dbk:screenshot * - + dbk:subtitle (dbk:subtitle) = dbk:subtitle * - - dbk:label (String) - - dbk:status (String) - -[dbk:refsynopsisdiv] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled - + dbk:anchor (dbk:anchor) = dbk:anchor * - + dbk:annotation (dbk:annotation) = dbk:annotation * - + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead * - + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject * - + dbk:refsect2 (dbk:refsect2) = dbk:refsect2 * - + dbk:refsection (dbk:refsection) = dbk:refsection * - + dbk:remark (dbk:remark) = dbk:remark * - + dbk:revhistory (dbk:revhistory) = dbk:revhistory * - + dbk:screenshot (dbk:screenshot) = dbk:screenshot * - + dbk:subtitle (dbk:subtitle) = dbk:subtitle * - -[dbk:releaseinfo] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines - -[dbk:remark] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines - -[dbk:replaceable] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines - + dbk:co (dbk:co) = dbk:co * - - dbk:class (String) - -[dbk:returnvalue] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines - -[dbk:revdescription] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements - + dbk:anchor (dbk:anchor) = dbk:anchor * - + dbk:annotation (dbk:annotation) = dbk:annotation * - + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead * - + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject * - + dbk:remark (dbk:remark) = dbk:remark * - + dbk:revhistory (dbk:revhistory) = dbk:revhistory * - + dbk:screenshot (dbk:screenshot) = dbk:screenshot * - -[dbk:revhistory] > argeodbk:base, argeodbk:linkingAttributes, argeodbk:titled - + dbk:revision (dbk:revision) = dbk:revision * - -[dbk:revision] > argeodbk:base, argeodbk:linkingAttributes - + dbk:author (dbk:author) = dbk:author * - + dbk:authorinitials (dbk:authorinitials) = dbk:authorinitials * - + dbk:date (dbk:date) = dbk:date - + dbk:revdescription (dbk:revdescription) = dbk:revdescription - + dbk:revnumber (dbk:revnumber) = dbk:revnumber - + dbk:revremark (dbk:revremark) = dbk:revremark - -[dbk:revnumber] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines - -[dbk:revremark] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines - -[dbk:rhs] > argeodbk:base, argeodbk:linkingAttributes - + dbk:lineannotation (dbk:lineannotation) = dbk:lineannotation * - + dbk:nonterminal (dbk:nonterminal) = dbk:nonterminal * - + dbk:sbr (dbk:sbr) = dbk:sbr * - + jcr:xmltext (dbk:xmltext) = dbk:xmltext * - -[dbk:row] > argeodbk:base, argeodbk:linkingAttributes - + dbk:entry (dbk:entry) = dbk:entry * - + dbk:entrytbl (dbk:entrytbl) = dbk:entrytbl * - - dbk:rowsep (String) - - dbk:valign (String) - -[dbk:sbr] > argeodbk:base - -[dbk:screen] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:markupInlines, argeodbk:publishingInlines, argeodbk:techDocInlines, argeodbk:ubiquitousInlines - + dbk:co (dbk:co) = dbk:co * - + dbk:info (dbk:info) = dbk:info * - + dbk:lineannotation (dbk:lineannotation) = dbk:lineannotation * - + dbk:textobject (dbk:textobject) = dbk:textobject * - - dbk:continuation (String) - - dbk:language (String) - - dbk:linenumbering (String) - - dbk:startinglinenumber (String) - - dbk:width (String) - - xml:space (String) - -[dbk:screenco] > argeodbk:base, argeodbk:linkingAttributes - + dbk:areaspec (dbk:areaspec) = dbk:areaspec - + dbk:calloutlist (dbk:calloutlist) = dbk:calloutlist * - + dbk:info (dbk:info) = dbk:info - + dbk:screen (dbk:screen) = dbk:screen - -[dbk:screenshot] > argeodbk:base, argeodbk:linkingAttributes, argeodbk:titled - + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject - + dbk:subtitle (dbk:subtitle) = dbk:subtitle * - -[dbk:secondary] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:markupInlines, argeodbk:publishingInlines, argeodbk:techDocInlines, argeodbk:ubiquitousInlines - - dbk:sortas (String) - -[dbk:secondaryie] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:markupInlines, argeodbk:publishingInlines, argeodbk:techDocInlines, argeodbk:ubiquitousInlines - - dbk:linkends (String) - -[dbk:sect1] > argeodbk:abstractSection, argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled - + dbk:anchor (dbk:anchor) = dbk:anchor * - + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject * - + dbk:screenshot (dbk:screenshot) = dbk:screenshot * - + dbk:sect2 (dbk:sect2) = dbk:sect2 * - + dbk:simplesect (dbk:simplesect) = dbk:simplesect * - + dbk:simplesect (dbk:simplesect) = dbk:simplesect * - -[dbk:sect2] > argeodbk:abstractSection, argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled - + dbk:anchor (dbk:anchor) = dbk:anchor * - + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject * - + dbk:screenshot (dbk:screenshot) = dbk:screenshot * - + dbk:sect3 (dbk:sect3) = dbk:sect3 * - + dbk:simplesect (dbk:simplesect) = dbk:simplesect * - + dbk:simplesect (dbk:simplesect) = dbk:simplesect * - -[dbk:sect3] > argeodbk:abstractSection, argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled - + dbk:anchor (dbk:anchor) = dbk:anchor * - + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject * - + dbk:screenshot (dbk:screenshot) = dbk:screenshot * - + dbk:sect4 (dbk:sect4) = dbk:sect4 * - + dbk:simplesect (dbk:simplesect) = dbk:simplesect * - + dbk:simplesect (dbk:simplesect) = dbk:simplesect * - -[dbk:sect4] > argeodbk:abstractSection, argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled - + dbk:anchor (dbk:anchor) = dbk:anchor * - + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject * - + dbk:screenshot (dbk:screenshot) = dbk:screenshot * - + dbk:sect5 (dbk:sect5) = dbk:sect5 * - + dbk:simplesect (dbk:simplesect) = dbk:simplesect * - + dbk:simplesect (dbk:simplesect) = dbk:simplesect * - -[dbk:sect5] > argeodbk:abstractSection, argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled - + dbk:anchor (dbk:anchor) = dbk:anchor * - + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject * - + dbk:screenshot (dbk:screenshot) = dbk:screenshot * - + dbk:simplesect (dbk:simplesect) = dbk:simplesect * - + dbk:simplesect (dbk:simplesect) = dbk:simplesect * - -[dbk:section] > argeodbk:abstractSection, argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled - + dbk:anchor (dbk:anchor) = dbk:anchor * - + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject * - + dbk:refentry (dbk:refentry) = dbk:refentry * - + dbk:screenshot (dbk:screenshot) = dbk:screenshot * - + dbk:section (dbk:section) = dbk:section * - + dbk:simplesect (dbk:simplesect) = dbk:simplesect * - + dbk:simplesect (dbk:simplesect) = dbk:simplesect * - -[dbk:see] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:markupInlines, argeodbk:publishingInlines, argeodbk:techDocInlines, argeodbk:ubiquitousInlines - -[dbk:seealso] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:markupInlines, argeodbk:publishingInlines, argeodbk:techDocInlines, argeodbk:ubiquitousInlines - -[dbk:seealsoie] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:markupInlines, argeodbk:publishingInlines, argeodbk:techDocInlines, argeodbk:ubiquitousInlines - - dbk:linkends (String) - -[dbk:seeie] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:markupInlines, argeodbk:publishingInlines, argeodbk:techDocInlines, argeodbk:ubiquitousInlines - -[dbk:seg] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:markupInlines, argeodbk:publishingInlines, argeodbk:techDocInlines, argeodbk:ubiquitousInlines - -[dbk:seglistitem] > argeodbk:base, argeodbk:linkingAttributes - + dbk:seg (dbk:seg) = dbk:seg * - -[dbk:segmentedlist] > argeodbk:base, argeodbk:linkingAttributes, argeodbk:titled - + dbk:seglistitem (dbk:seglistitem) = dbk:seglistitem * - + dbk:segtitle (dbk:segtitle) = dbk:segtitle * - -[dbk:segtitle] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:markupInlines, argeodbk:publishingInlines, argeodbk:techDocInlines, argeodbk:ubiquitousInlines - -[dbk:seriesvolnums] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines - -[dbk:set] > argeodbk:base, argeodbk:linkingAttributes, argeodbk:titled - + dbk:book (dbk:book) = dbk:book * - + dbk:set (dbk:set) = dbk:set * - + dbk:setindex (dbk:setindex) = dbk:setindex - + dbk:subtitle (dbk:subtitle) = dbk:subtitle * - + dbk:toc (dbk:toc) = dbk:toc - - dbk:label (String) - - dbk:status (String) - -[dbk:setindex] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled - + dbk:anchor (dbk:anchor) = dbk:anchor * - + dbk:annotation (dbk:annotation) = dbk:annotation * - + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead * - + dbk:indexdiv (dbk:indexdiv) = dbk:indexdiv * - + dbk:indexentry (dbk:indexentry) = dbk:indexentry * - + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject * - + dbk:remark (dbk:remark) = dbk:remark * - + dbk:revhistory (dbk:revhistory) = dbk:revhistory * - + dbk:screenshot (dbk:screenshot) = dbk:screenshot * - + dbk:subtitle (dbk:subtitle) = dbk:subtitle * - - dbk:label (String) - - dbk:status (String) - - dbk:type (String) - -[dbk:shortaffil] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines - -[dbk:shortcut] > argeodbk:base, argeodbk:linkingAttributes - + dbk:keycap (dbk:keycap) = dbk:keycap * - + dbk:keycombo (dbk:keycombo) = dbk:keycombo * - + dbk:keysym (dbk:keysym) = dbk:keysym * - + dbk:mousebutton (dbk:mousebutton) = dbk:mousebutton * - - dbk:action (String) - - dbk:otheraction (String) - -[dbk:sidebar] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled - + dbk:anchor (dbk:anchor) = dbk:anchor * - + dbk:annotation (dbk:annotation) = dbk:annotation * - + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead * - + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject * - + dbk:remark (dbk:remark) = dbk:remark * - + dbk:revhistory (dbk:revhistory) = dbk:revhistory * - + dbk:screenshot (dbk:screenshot) = dbk:screenshot * - -[dbk:simpara] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:markupInlines, argeodbk:publishingInlines, argeodbk:techDocInlines, argeodbk:ubiquitousInlines - + dbk:info (dbk:info) = dbk:info * - -[dbk:simplelist] > argeodbk:base, argeodbk:linkingAttributes - + dbk:member (dbk:member) = dbk:member * - - dbk:columns (String) - - dbk:type (String) - -[dbk:simplemsgentry] > argeodbk:base, argeodbk:linkingAttributes - + dbk:msgexplan (dbk:msgexplan) = dbk:msgexplan * - + dbk:msgtext (dbk:msgtext) = dbk:msgtext - - dbk:msgaud (String) - - dbk:msglevel (String) - - dbk:msgorig (String) - -[dbk:simplesect] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled - + dbk:anchor (dbk:anchor) = dbk:anchor * - + dbk:annotation (dbk:annotation) = dbk:annotation * - + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead * - + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject * - + dbk:remark (dbk:remark) = dbk:remark * - + dbk:revhistory (dbk:revhistory) = dbk:revhistory * - + dbk:screenshot (dbk:screenshot) = dbk:screenshot * - + dbk:subtitle (dbk:subtitle) = dbk:subtitle * - - dbk:label (String) - - dbk:status (String) - -[dbk:spanspec] > argeodbk:base, argeodbk:linkingAttributes - - dbk:align (String) - - dbk:char (String) - - dbk:charoff (String) - - dbk:colsep (String) - - dbk:nameend (String) - - dbk:namest (String) - - dbk:rowsep (String) - - dbk:spanname (String) - -[dbk:state] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines - -[dbk:step] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled - + dbk:anchor (dbk:anchor) = dbk:anchor * - + dbk:annotation (dbk:annotation) = dbk:annotation * - + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead * - + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject * - + dbk:remark (dbk:remark) = dbk:remark * - + dbk:revhistory (dbk:revhistory) = dbk:revhistory * - + dbk:screenshot (dbk:screenshot) = dbk:screenshot * - + dbk:stepalternatives (dbk:stepalternatives) = dbk:stepalternatives - + dbk:substeps (dbk:substeps) = dbk:substeps - - dbk:performance (String) - -[dbk:stepalternatives] > argeodbk:base, argeodbk:linkingAttributes - + dbk:info (dbk:info) = dbk:info - + dbk:step (dbk:step) = dbk:step * - - dbk:performance (String) - -[dbk:street] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines - -[dbk:subject] > argeodbk:base, argeodbk:linkingAttributes - + dbk:subjectterm (dbk:subjectterm) = dbk:subjectterm * - - dbk:weight (String) - -[dbk:subjectset] > argeodbk:base, argeodbk:linkingAttributes - + dbk:subject (dbk:subject) = dbk:subject * - - dbk:scheme (String) - -[dbk:subjectterm] > argeodbk:base, argeodbk:linkingAttributes - + jcr:xmltext (dbk:xmltext) = dbk:xmltext * - -[dbk:subscript] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines - -[dbk:substeps] > argeodbk:base, argeodbk:linkingAttributes - + dbk:step (dbk:step) = dbk:step * - - dbk:performance (String) - -[dbk:subtitle] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:markupInlines, argeodbk:publishingInlines, argeodbk:techDocInlines, argeodbk:ubiquitousInlines - -[dbk:superscript] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines - -[dbk:surname] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines - -[dbk:symbol] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines - - dbk:class (String) - -[dbk:synopfragment] > argeodbk:base, argeodbk:linkingAttributes - + dbk:arg (dbk:arg) = dbk:arg * - + dbk:group (dbk:group) = dbk:group * - -[dbk:synopfragmentref] > argeodbk:base, argeodbk:linkingAttributes - + jcr:xmltext (dbk:xmltext) = dbk:xmltext * - -[dbk:synopsis] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:markupInlines, argeodbk:publishingInlines, argeodbk:techDocInlines, argeodbk:ubiquitousInlines - + dbk:co (dbk:co) = dbk:co * - + dbk:info (dbk:info) = dbk:info * - + dbk:lineannotation (dbk:lineannotation) = dbk:lineannotation * - + dbk:textobject (dbk:textobject) = dbk:textobject * - - dbk:continuation (String) - - dbk:label (String) - - dbk:language (String) - - dbk:linenumbering (String) - - dbk:startinglinenumber (String) - - xml:space (String) - -[dbk:systemitem] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines - + dbk:co (dbk:co) = dbk:co * - - dbk:class (String) - -[dbk:table] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:titled - + dbk:caption (dbk:caption) = dbk:caption - + dbk:col (dbk:col) = dbk:col * - + dbk:colgroup (dbk:colgroup) = dbk:colgroup * - + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject * - + dbk:tbody (dbk:tbody) = dbk:tbody * - + dbk:textobject (dbk:textobject) = dbk:textobject * - + dbk:tfoot (dbk:tfoot) = dbk:tfoot - + dbk:tgroup (dbk:tgroup) = dbk:tgroup * - + dbk:thead (dbk:thead) = dbk:thead - + dbk:tr (dbk:tr) = dbk:tr * - - dbk:border (String) - - dbk:cellpadding (String) - - dbk:cellspacing (String) - - dbk:class (String) - - dbk:colsep (String) - - dbk:floatstyle (String) - - dbk:frame (String) - - dbk:label (String) - - dbk:lang (String) - - dbk:onclick (String) - - dbk:ondblclick (String) - - dbk:onkeydown (String) - - dbk:onkeypress (String) - - dbk:onkeyup (String) - - dbk:onmousedown (String) - - dbk:onmousemove (String) - - dbk:onmouseout (String) - - dbk:onmouseover (String) - - dbk:onmouseup (String) - - dbk:orient (String) - - dbk:pgwide (String) - - dbk:rowheader (String) - - dbk:rowsep (String) - - dbk:rules (String) - - dbk:shortentry (String) - - dbk:style (String) - - dbk:summary (String) - - dbk:tabstyle (String) - - dbk:title (String) - - dbk:tocentry (String) - - dbk:width (String) - -[dbk:tag] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines - - dbk:class (String) - - dbk:namespace (String) - -[dbk:task] > argeodbk:base, argeodbk:linkingAttributes, argeodbk:titled - + dbk:example (dbk:example) = dbk:example * - + dbk:procedure (dbk:procedure) = dbk:procedure - + dbk:subtitle (dbk:subtitle) = dbk:subtitle * - + dbk:taskprerequisites (dbk:taskprerequisites) = dbk:taskprerequisites - + dbk:taskrelated (dbk:taskrelated) = dbk:taskrelated - + dbk:tasksummary (dbk:tasksummary) = dbk:tasksummary - -[dbk:taskprerequisites] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled - + dbk:anchor (dbk:anchor) = dbk:anchor * - + dbk:annotation (dbk:annotation) = dbk:annotation * - + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead * - + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject * - + dbk:remark (dbk:remark) = dbk:remark * - + dbk:revhistory (dbk:revhistory) = dbk:revhistory * - + dbk:screenshot (dbk:screenshot) = dbk:screenshot * - -[dbk:taskrelated] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled - + dbk:anchor (dbk:anchor) = dbk:anchor * - + dbk:annotation (dbk:annotation) = dbk:annotation * - + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead * - + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject * - + dbk:remark (dbk:remark) = dbk:remark * - + dbk:revhistory (dbk:revhistory) = dbk:revhistory * - + dbk:screenshot (dbk:screenshot) = dbk:screenshot * - -[dbk:tasksummary] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled - + dbk:anchor (dbk:anchor) = dbk:anchor * - + dbk:annotation (dbk:annotation) = dbk:annotation * - + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead * - + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject * - + dbk:remark (dbk:remark) = dbk:remark * - + dbk:revhistory (dbk:revhistory) = dbk:revhistory * - + dbk:screenshot (dbk:screenshot) = dbk:screenshot * - -[dbk:tbody] > argeodbk:base, argeodbk:linkingAttributes - + dbk:row (dbk:row) = dbk:row * - + dbk:tr (dbk:tr) = dbk:tr * - - dbk:align (String) - - dbk:char (String) - - dbk:charoff (String) - - dbk:class (String) - - dbk:lang (String) - - dbk:onclick (String) - - dbk:ondblclick (String) - - dbk:onkeydown (String) - - dbk:onkeypress (String) - - dbk:onkeyup (String) - - dbk:onmousedown (String) - - dbk:onmousemove (String) - - dbk:onmouseout (String) - - dbk:onmouseover (String) - - dbk:onmouseup (String) - - dbk:style (String) - - dbk:title (String) - - dbk:valign (String) - -[dbk:td] > argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:listElements, argeodbk:markupInlines, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:publishingInlines, argeodbk:techDocElements, argeodbk:techDocInlines, argeodbk:ubiquitousInlines - + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead * - + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject * - + dbk:revhistory (dbk:revhistory) = dbk:revhistory * - + dbk:screenshot (dbk:screenshot) = dbk:screenshot * - - dbk:abbr (String) - - dbk:align (String) - - dbk:annotations (String) - - dbk:arch (String) - - dbk:audience (String) - - dbk:axis (String) - - dbk:char (String) - - dbk:charoff (String) - - dbk:class (String) - - dbk:colspan (String) - - dbk:condition (String) - - dbk:conformance (String) - - dbk:dir (String) - - dbk:headers (String) - - dbk:lang (String) - - dbk:onclick (String) - - dbk:ondblclick (String) - - dbk:onkeydown (String) - - dbk:onkeypress (String) - - dbk:onkeyup (String) - - dbk:onmousedown (String) - - dbk:onmousemove (String) - - dbk:onmouseout (String) - - dbk:onmouseover (String) - - dbk:onmouseup (String) - - dbk:os (String) - - dbk:remap (String) - - dbk:revision (String) - - dbk:revisionflag (String) - - dbk:rowspan (String) - - dbk:scope (String) - - dbk:security (String) - - dbk:style (String) - - dbk:title (String) - - dbk:userlevel (String) - - dbk:valign (String) - - dbk:vendor (String) - - dbk:version (String) - - dbk:wordsize (String) - - dbk:xreflabel (String) - - xml:base (String) - - xml:id (String) - - xml:lang (String) - -[dbk:term] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:markupInlines, argeodbk:publishingInlines, argeodbk:techDocInlines, argeodbk:ubiquitousInlines - -[dbk:termdef] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:markupInlines, argeodbk:publishingInlines, argeodbk:techDocInlines, argeodbk:ubiquitousInlines - - dbk:baseform (String) - - dbk:sortas (String) - -[dbk:tertiary] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:markupInlines, argeodbk:publishingInlines, argeodbk:techDocInlines, argeodbk:ubiquitousInlines - - dbk:sortas (String) - -[dbk:tertiaryie] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:markupInlines, argeodbk:publishingInlines, argeodbk:techDocInlines, argeodbk:ubiquitousInlines - - dbk:linkends (String) - -[dbk:textdata] > argeodbk:base - + dbk:info (dbk:info) = dbk:info - - dbk:encoding (String) - - dbk:entityref (String) - - dbk:fileref (String) - - dbk:format (String) - -[dbk:textobject] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements - + dbk:anchor (dbk:anchor) = dbk:anchor * - + dbk:annotation (dbk:annotation) = dbk:annotation * - + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead * - + dbk:info (dbk:info) = dbk:info - + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject * - + dbk:phrase (dbk:phrase) = dbk:phrase - + dbk:remark (dbk:remark) = dbk:remark * - + dbk:revhistory (dbk:revhistory) = dbk:revhistory * - + dbk:screenshot (dbk:screenshot) = dbk:screenshot * - + dbk:textdata (dbk:textdata) = dbk:textdata - -[dbk:tfoot] > argeodbk:base, argeodbk:linkingAttributes - + dbk:colspec (dbk:colspec) = dbk:colspec * - + dbk:row (dbk:row) = dbk:row * - + dbk:tr (dbk:tr) = dbk:tr * - - dbk:align (String) - - dbk:char (String) - - dbk:charoff (String) - - dbk:class (String) - - dbk:lang (String) - - dbk:onclick (String) - - dbk:ondblclick (String) - - dbk:onkeydown (String) - - dbk:onkeypress (String) - - dbk:onkeyup (String) - - dbk:onmousedown (String) - - dbk:onmousemove (String) - - dbk:onmouseout (String) - - dbk:onmouseover (String) - - dbk:onmouseup (String) - - dbk:style (String) - - dbk:title (String) - - dbk:valign (String) - -[dbk:tgroup] > argeodbk:base, argeodbk:linkingAttributes - + dbk:colspec (dbk:colspec) = dbk:colspec * - + dbk:spanspec (dbk:spanspec) = dbk:spanspec * - + dbk:tbody (dbk:tbody) = dbk:tbody - + dbk:tfoot (dbk:tfoot) = dbk:tfoot - + dbk:thead (dbk:thead) = dbk:thead - - dbk:align (String) - - dbk:char (String) - - dbk:charoff (String) - - dbk:cols (String) - - dbk:colsep (String) - - dbk:rowsep (String) - - dbk:tgroupstyle (String) - -[dbk:th] > argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:listElements, argeodbk:markupInlines, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:publishingInlines, argeodbk:techDocElements, argeodbk:techDocInlines, argeodbk:ubiquitousInlines - + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead * - + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject * - + dbk:revhistory (dbk:revhistory) = dbk:revhistory * - + dbk:screenshot (dbk:screenshot) = dbk:screenshot * - - dbk:abbr (String) - - dbk:align (String) - - dbk:annotations (String) - - dbk:arch (String) - - dbk:audience (String) - - dbk:axis (String) - - dbk:char (String) - - dbk:charoff (String) - - dbk:class (String) - - dbk:colspan (String) - - dbk:condition (String) - - dbk:conformance (String) - - dbk:dir (String) - - dbk:headers (String) - - dbk:lang (String) - - dbk:onclick (String) - - dbk:ondblclick (String) - - dbk:onkeydown (String) - - dbk:onkeypress (String) - - dbk:onkeyup (String) - - dbk:onmousedown (String) - - dbk:onmousemove (String) - - dbk:onmouseout (String) - - dbk:onmouseover (String) - - dbk:onmouseup (String) - - dbk:os (String) - - dbk:remap (String) - - dbk:revision (String) - - dbk:revisionflag (String) - - dbk:rowspan (String) - - dbk:scope (String) - - dbk:security (String) - - dbk:style (String) - - dbk:title (String) - - dbk:userlevel (String) - - dbk:valign (String) - - dbk:vendor (String) - - dbk:version (String) - - dbk:wordsize (String) - - dbk:xreflabel (String) - - xml:base (String) - - xml:id (String) - - xml:lang (String) - -[dbk:thead] > argeodbk:base, argeodbk:linkingAttributes - + dbk:colspec (dbk:colspec) = dbk:colspec * - + dbk:row (dbk:row) = dbk:row * - + dbk:tr (dbk:tr) = dbk:tr * - - dbk:align (String) - - dbk:char (String) - - dbk:charoff (String) - - dbk:class (String) - - dbk:lang (String) - - dbk:onclick (String) - - dbk:ondblclick (String) - - dbk:onkeydown (String) - - dbk:onkeypress (String) - - dbk:onkeyup (String) - - dbk:onmousedown (String) - - dbk:onmousemove (String) - - dbk:onmouseout (String) - - dbk:onmouseover (String) - - dbk:onmouseup (String) - - dbk:style (String) - - dbk:title (String) - - dbk:valign (String) - -[dbk:tip] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled - + dbk:anchor (dbk:anchor) = dbk:anchor * - + dbk:annotation (dbk:annotation) = dbk:annotation * - + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead * - + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject * - + dbk:remark (dbk:remark) = dbk:remark * - + dbk:revhistory (dbk:revhistory) = dbk:revhistory * - + dbk:screenshot (dbk:screenshot) = dbk:screenshot * - -[dbk:title] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:markupInlines, argeodbk:publishingInlines, argeodbk:techDocInlines, argeodbk:ubiquitousInlines - -[dbk:titleabbrev] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:markupInlines, argeodbk:publishingInlines, argeodbk:techDocInlines, argeodbk:ubiquitousInlines - -[dbk:toc] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled - + dbk:anchor (dbk:anchor) = dbk:anchor * - + dbk:annotation (dbk:annotation) = dbk:annotation * - + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead * - + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject * - + dbk:remark (dbk:remark) = dbk:remark * - + dbk:revhistory (dbk:revhistory) = dbk:revhistory * - + dbk:screenshot (dbk:screenshot) = dbk:screenshot * - + dbk:tocdiv (dbk:tocdiv) = dbk:tocdiv * - + dbk:tocentry (dbk:tocentry) = dbk:tocentry * - -[dbk:tocdiv] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled - + dbk:anchor (dbk:anchor) = dbk:anchor * - + dbk:annotation (dbk:annotation) = dbk:annotation * - + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead * - + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject * - + dbk:remark (dbk:remark) = dbk:remark * - + dbk:revhistory (dbk:revhistory) = dbk:revhistory * - + dbk:screenshot (dbk:screenshot) = dbk:screenshot * - + dbk:subtitle (dbk:subtitle) = dbk:subtitle * - + dbk:tocdiv (dbk:tocdiv) = dbk:tocdiv * - + dbk:tocentry (dbk:tocentry) = dbk:tocentry * - - dbk:pagenum (String) - -[dbk:tocentry] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:markupInlines, argeodbk:publishingInlines, argeodbk:techDocInlines, argeodbk:ubiquitousInlines - - dbk:pagenum (String) - -[dbk:token] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines - -[dbk:tr] > nt:base - + dbk:td (dbk:td) = dbk:td * - + dbk:th (dbk:th) = dbk:th * - - dbk:align (String) - - dbk:annotations (String) - - dbk:arch (String) - - dbk:audience (String) - - dbk:char (String) - - dbk:charoff (String) - - dbk:class (String) - - dbk:condition (String) - - dbk:conformance (String) - - dbk:dir (String) - - dbk:lang (String) - - dbk:onclick (String) - - dbk:ondblclick (String) - - dbk:onkeydown (String) - - dbk:onkeypress (String) - - dbk:onkeyup (String) - - dbk:onmousedown (String) - - dbk:onmousemove (String) - - dbk:onmouseout (String) - - dbk:onmouseover (String) - - dbk:onmouseup (String) - - dbk:os (String) - - dbk:remap (String) - - dbk:revision (String) - - dbk:revisionflag (String) - - dbk:security (String) - - dbk:style (String) - - dbk:title (String) - - dbk:userlevel (String) - - dbk:valign (String) - - dbk:vendor (String) - - dbk:version (String) - - dbk:wordsize (String) - - dbk:xreflabel (String) - - xml:base (String) - - xml:id (String) - - xml:lang (String) - -[dbk:trademark] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines - - dbk:class (String) - -[dbk:type] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines - -[dbk:uri] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines - - dbk:type (String) - -[dbk:userinput] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:markupInlines, argeodbk:ubiquitousInlines - + dbk:accel (dbk:accel) = dbk:accel * - + dbk:co (dbk:co) = dbk:co * - + dbk:command (dbk:command) = dbk:command * - + dbk:computeroutput (dbk:computeroutput) = dbk:computeroutput * - + dbk:envar (dbk:envar) = dbk:envar * - + dbk:filename (dbk:filename) = dbk:filename * - + dbk:guibutton (dbk:guibutton) = dbk:guibutton * - + dbk:guiicon (dbk:guiicon) = dbk:guiicon * - + dbk:guilabel (dbk:guilabel) = dbk:guilabel * - + dbk:guimenu (dbk:guimenu) = dbk:guimenu * - + dbk:guimenuitem (dbk:guimenuitem) = dbk:guimenuitem * - + dbk:guisubmenu (dbk:guisubmenu) = dbk:guisubmenu * - + dbk:keycap (dbk:keycap) = dbk:keycap * - + dbk:keycode (dbk:keycode) = dbk:keycode * - + dbk:keycombo (dbk:keycombo) = dbk:keycombo * - + dbk:keysym (dbk:keysym) = dbk:keysym * - + dbk:menuchoice (dbk:menuchoice) = dbk:menuchoice * - + dbk:mousebutton (dbk:mousebutton) = dbk:mousebutton * - + dbk:nonterminal (dbk:nonterminal) = dbk:nonterminal * - + dbk:option (dbk:option) = dbk:option * - + dbk:optional (dbk:optional) = dbk:optional * - + dbk:package (dbk:package) = dbk:package * - + dbk:parameter (dbk:parameter) = dbk:parameter * - + dbk:prompt (dbk:prompt) = dbk:prompt * - + dbk:property (dbk:property) = dbk:property * - + dbk:replaceable (dbk:replaceable) = dbk:replaceable * - + dbk:shortcut (dbk:shortcut) = dbk:shortcut * - + dbk:systemitem (dbk:systemitem) = dbk:systemitem * - + dbk:termdef (dbk:termdef) = dbk:termdef * - + dbk:userinput (dbk:userinput) = dbk:userinput * - + jcr:xmltext (dbk:xmltext) = dbk:xmltext * - -[dbk:varargs] > argeodbk:base, argeodbk:linkingAttributes - -[dbk:variablelist] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled - + dbk:anchor (dbk:anchor) = dbk:anchor * - + dbk:annotation (dbk:annotation) = dbk:annotation * - + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead * - + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject * - + dbk:remark (dbk:remark) = dbk:remark * - + dbk:revhistory (dbk:revhistory) = dbk:revhistory * - + dbk:screenshot (dbk:screenshot) = dbk:screenshot * - + dbk:varlistentry (dbk:varlistentry) = dbk:varlistentry * - - dbk:spacing (String) - - dbk:termlength (String) - -[dbk:varlistentry] > argeodbk:base, argeodbk:linkingAttributes - + dbk:listitem (dbk:listitem) = dbk:listitem - + dbk:term (dbk:term) = dbk:term * - -[dbk:varname] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines - -[dbk:videodata] > argeodbk:base - + dbk:info (dbk:info) = dbk:info - - dbk:align (String) - - dbk:contentdepth (String) - - dbk:contentwidth (String) - - dbk:depth (String) - - dbk:entityref (String) - - dbk:fileref (String) - - dbk:format (String) - - dbk:scale (String) - - dbk:scalefit (String) - - dbk:valign (String) - - dbk:width (String) - -[dbk:videoobject] > argeodbk:base, argeodbk:linkingAttributes - + dbk:info (dbk:info) = dbk:info - + dbk:videodata (dbk:videodata) = dbk:videodata - -[dbk:void] > argeodbk:base, argeodbk:linkingAttributes - -[dbk:volumenum] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines - -[dbk:warning] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled - + dbk:anchor (dbk:anchor) = dbk:anchor * - + dbk:annotation (dbk:annotation) = dbk:annotation * - + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead * - + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject * - + dbk:remark (dbk:remark) = dbk:remark * - + dbk:revhistory (dbk:revhistory) = dbk:revhistory * - + dbk:screenshot (dbk:screenshot) = dbk:screenshot * - -[dbk:wordasword] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines - -[dbk:xmltext] > nt:base - - jcr:xmlcharacters (String) - -[dbk:xref] > argeodbk:base, argeodbk:linkingAttributes - - dbk:endterm (Reference) - - dbk:xrefstyle (String) - -[dbk:year] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines - -[xs:anyType] > nt:base - + * (nt:base) - + jcr:xmltext (dbk:xmltext) = dbk:xmltext * - - * (undefined) - - diff --git a/org.argeo.server.jcr/src/org/argeo/jcr/docbook/docbook.cnd b/org.argeo.server.jcr/src/org/argeo/jcr/docbook/docbook.cnd deleted file mode 100644 index c737cee63..000000000 --- a/org.argeo.server.jcr/src/org/argeo/jcr/docbook/docbook.cnd +++ /dev/null @@ -1,531 +0,0 @@ - - - - -[argeodbk:titled] -mixin - + dbk:info (dbk:info) = dbk:info * - + dbk:title (dbk:title) = dbk:title * - -[argeodbk:linkingAttributes] -mixin - - dbk:linkend (String) - - xlink:actuate (String) - - xlink:arcrole (String) - - xlink:href (String) - - xlink:role (String) - - xlink:show (String) - - xlink:title (String) - - xlink:type (String) - -[argeodbk:freeText] -mixin - + dbk:phrase (dbk:phrase) = dbk:phrase * - + dbk:replaceable (dbk:replaceable) = dbk:replaceable * - + jcr:xmltext (dbk:xmltext) = dbk:xmltext * - -[argeodbk:markupInlines] -mixin - -[argeodbk:listElements] -mixin - + dbk:itemizedlist (dbk:itemizedlist) = dbk:itemizedlist * - + dbk:orderedlist (dbk:orderedlist) = dbk:orderedlist * - + dbk:simplelist (dbk:simplelist) = dbk:simplelist * - -[argeodbk:paragraphElements] -mixin - + dbk:para (dbk:para) = dbk:para * - -[argeodbk:indexingInlines] -mixin - -[argeodbk:techDocElements] -mixin - + dbk:table (dbk:table) = dbk:table * - -[argeodbk:techDocInlines] -mixin - -[argeodbk:publishingElements] -mixin - -[argeodbk:ubiquitousInlines] -mixin - + dbk:alt (dbk:alt) = dbk:alt * - + dbk:anchor (dbk:anchor) = dbk:anchor * - + dbk:annotation (dbk:annotation) = dbk:annotation * - + dbk:biblioref (dbk:biblioref) = dbk:biblioref * - + dbk:inlinemediaobject (dbk:inlinemediaobject) = dbk:inlinemediaobject * - + dbk:link (dbk:link) = dbk:link * - + dbk:olink (dbk:olink) = dbk:olink * - + dbk:remark (dbk:remark) = dbk:remark * - + dbk:subscript (dbk:subscript) = dbk:subscript * - + dbk:superscript (dbk:superscript) = dbk:superscript * - + dbk:xref (dbk:xref) = dbk:xref * - -[argeodbk:abstractSection] -mixin - + dbk:annotation (dbk:annotation) = dbk:annotation * - + dbk:remark (dbk:remark) = dbk:remark * - + dbk:subtitle (dbk:subtitle) = dbk:subtitle * - - dbk:label (String) - - dbk:status (String) - -[argeodbk:bibliographyInlines] -mixin - + dbk:author (dbk:author) = dbk:author * - + dbk:editor (dbk:editor) = dbk:editor * - + dbk:orgname (dbk:orgname) = dbk:orgname * - + dbk:personname (dbk:personname) = dbk:personname * - -[argeodbk:publishingInlines] -mixin - + dbk:emphasis (dbk:emphasis) = dbk:emphasis * - -[argeodbk:base] -abstract - - dbk:annotations (String) - - dbk:arch (String) - - dbk:audience (String) - - dbk:condition (String) - - dbk:conformance (String) - - dbk:dir (String) - - dbk:os (String) - - dbk:remap (String) - - dbk:revision (String) - - dbk:revisionflag (String) - - dbk:role (String) - - dbk:security (String) - - dbk:userlevel (String) - - dbk:vendor (String) - - dbk:version (String) - - dbk:wordsize (String) - - dbk:xreflabel (String) -// - {http://www.w3.org/XML/1998/namespace}base (String) -// - {http://www.w3.org/XML/1998/namespace}id (String) -// - {http://www.w3.org/XML/1998/namespace}lang (String) - -[dbk:alt] > argeodbk:base - + dbk:inlinemediaobject (dbk:inlinemediaobject) = dbk:inlinemediaobject * - + jcr:xmltext (dbk:xmltext) = dbk:xmltext * - -[dbk:anchor] > argeodbk:base - -[dbk:annotation] > argeodbk:base, argeodbk:indexingInlines, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled - + dbk:anchor (dbk:anchor) = dbk:anchor * - + dbk:annotation (dbk:annotation) = dbk:annotation * - + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject * - + dbk:remark (dbk:remark) = dbk:remark * - - dbk:annotates (String) - -[dbk:article] > argeodbk:abstractSection, argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled - + dbk:anchor (dbk:anchor) = dbk:anchor * - + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject * - + dbk:section (dbk:section) = dbk:section * - - dbk:class (String) - -[dbk:audiodata] > argeodbk:base - + dbk:info (dbk:info) = dbk:info - - dbk:entityref (String) - - dbk:fileref (String) - - dbk:format (String) - -[dbk:audioobject] > argeodbk:base, argeodbk:linkingAttributes - + dbk:audiodata (dbk:audiodata) = dbk:audiodata - + dbk:info (dbk:info) = dbk:info - -[dbk:author] > argeodbk:base, argeodbk:linkingAttributes - + dbk:orgdiv (dbk:orgdiv) = dbk:orgdiv * - + dbk:orgname (dbk:orgname) = dbk:orgname - + dbk:personblurb (dbk:personblurb) = dbk:personblurb * - + dbk:personname (dbk:personname) = dbk:personname - -[dbk:biblioref] > argeodbk:base, argeodbk:linkingAttributes - - dbk:begin (String) - - dbk:end (String) - - dbk:endterm (Reference) - - dbk:units (String) - - dbk:xrefstyle (String) - -[dbk:book] > argeodbk:base, argeodbk:linkingAttributes, argeodbk:titled - + dbk:article (dbk:article) = dbk:article * - + dbk:chapter (dbk:chapter) = dbk:chapter * - + dbk:subtitle (dbk:subtitle) = dbk:subtitle * - - dbk:label (String) - - dbk:status (String) - -[dbk:caption] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements - + dbk:anchor (dbk:anchor) = dbk:anchor * - + dbk:annotation (dbk:annotation) = dbk:annotation * - + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject * - + dbk:remark (dbk:remark) = dbk:remark * - + jcr:xmltext (dbk:xmltext) = dbk:xmltext * - - dbk:class (String) - - dbk:lang (String) - - dbk:onclick (String) - - dbk:ondblclick (String) - - dbk:onkeydown (String) - - dbk:onkeypress (String) - - dbk:onkeyup (String) - - dbk:onmousedown (String) - - dbk:onmousemove (String) - - dbk:onmouseout (String) - - dbk:onmouseover (String) - - dbk:onmouseup (String) - - dbk:style (String) - - dbk:title (String) - -[dbk:chapter] > argeodbk:abstractSection, argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled - + dbk:anchor (dbk:anchor) = dbk:anchor * - + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject * - + dbk:section (dbk:section) = dbk:section * - -[dbk:colspec] > argeodbk:base, argeodbk:linkingAttributes - - dbk:align (String) - - dbk:char (String) - - dbk:charoff (String) - - dbk:colname (String) - - dbk:colnum (String) - - dbk:colsep (String) - - dbk:colwidth (String) - - dbk:rowsep (String) - -[dbk:editor] > argeodbk:base, argeodbk:linkingAttributes - + dbk:orgdiv (dbk:orgdiv) = dbk:orgdiv * - + dbk:orgname (dbk:orgname) = dbk:orgname - + dbk:personblurb (dbk:personblurb) = dbk:personblurb * - + dbk:personname (dbk:personname) = dbk:personname - -[dbk:emphasis] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:markupInlines, argeodbk:publishingInlines, argeodbk:techDocInlines, argeodbk:ubiquitousInlines - -[dbk:entry] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:markupInlines, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:publishingInlines, argeodbk:techDocElements, argeodbk:techDocInlines, argeodbk:ubiquitousInlines - + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject * - - dbk:align (String) - - dbk:char (String) - - dbk:charoff (String) - - dbk:colname (String) - - dbk:colsep (String) - - dbk:morerows (String) - - dbk:nameend (String) - - dbk:namest (String) - - dbk:rotate (String) - - dbk:rowsep (String) - - dbk:spanname (String) - - dbk:valign (String) - -[dbk:entrytbl] > argeodbk:base, argeodbk:linkingAttributes - + dbk:colspec (dbk:colspec) = dbk:colspec * - + dbk:spanspec (dbk:spanspec) = dbk:spanspec * - + dbk:tbody (dbk:tbody) = dbk:tbody - + dbk:thead (dbk:thead) = dbk:thead - - dbk:align (String) - - dbk:char (String) - - dbk:charoff (String) - - dbk:colname (String) - - dbk:cols (String) - - dbk:colsep (String) - - dbk:nameend (String) - - dbk:namest (String) - - dbk:rowsep (String) - - dbk:spanname (String) - - dbk:tgroupstyle (String) - -[dbk:imagedata] > argeodbk:base - + dbk:info (dbk:info) = dbk:info - - dbk:align (String) - - dbk:contentdepth (String) - - dbk:contentwidth (String) - - dbk:depth (String) - - dbk:entityref (String) - - dbk:fileref (String) - - dbk:format (String) - - dbk:scale (String) - - dbk:scalefit (String) - - dbk:valign (String) - - dbk:width (String) - -[dbk:imageobject] > argeodbk:base, argeodbk:linkingAttributes - + dbk:imagedata (dbk:imagedata) = dbk:imagedata - + dbk:info (dbk:info) = dbk:info - -[dbk:info] > argeodbk:base - + dbk:annotation (dbk:annotation) = dbk:annotation * - + dbk:author (dbk:author) = dbk:author * - + dbk:editor (dbk:editor) = dbk:editor * - + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject * - + dbk:orgname (dbk:orgname) = dbk:orgname * - + dbk:subtitle (dbk:subtitle) = dbk:subtitle * - + dbk:title (dbk:title) = dbk:title * - -[dbk:inlinemediaobject] > argeodbk:base, argeodbk:linkingAttributes - + dbk:alt (dbk:alt) = dbk:alt - + dbk:audioobject (dbk:audioobject) = dbk:audioobject * - + dbk:imageobject (dbk:imageobject) = dbk:imageobject * - + dbk:info (dbk:info) = dbk:info - + dbk:textobject (dbk:textobject) = dbk:textobject * - + dbk:videoobject (dbk:videoobject) = dbk:videoobject * - -[dbk:itemizedlist] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled - + dbk:anchor (dbk:anchor) = dbk:anchor * - + dbk:annotation (dbk:annotation) = dbk:annotation * - + dbk:listitem (dbk:listitem) = dbk:listitem * - + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject * - + dbk:remark (dbk:remark) = dbk:remark * - - dbk:mark (String) - - dbk:spacing (String) - -[dbk:link] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:markupInlines, argeodbk:publishingInlines, argeodbk:techDocInlines, argeodbk:ubiquitousInlines - - dbk:endterm (Reference) - - dbk:xrefstyle (String) - -[dbk:listitem] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements - + dbk:anchor (dbk:anchor) = dbk:anchor * - + dbk:annotation (dbk:annotation) = dbk:annotation * - + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject * - + dbk:remark (dbk:remark) = dbk:remark * - - dbk:override (String) - -[dbk:mediaobject] > argeodbk:base, argeodbk:linkingAttributes - + dbk:alt (dbk:alt) = dbk:alt - + dbk:audioobject (dbk:audioobject) = dbk:audioobject * - + dbk:caption (dbk:caption) = dbk:caption - + dbk:imageobject (dbk:imageobject) = dbk:imageobject * - + dbk:info (dbk:info) = dbk:info - + dbk:textobject (dbk:textobject) = dbk:textobject * - + dbk:videoobject (dbk:videoobject) = dbk:videoobject * - -[dbk:olink] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:markupInlines, argeodbk:publishingInlines, argeodbk:techDocInlines, argeodbk:ubiquitousInlines - - dbk:localinfo (String) - - dbk:targetdoc (String) - - dbk:targetptr (String) - - dbk:type (String) - - dbk:xrefstyle (String) - -[dbk:orderedlist] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled - + dbk:anchor (dbk:anchor) = dbk:anchor * - + dbk:annotation (dbk:annotation) = dbk:annotation * - + dbk:listitem (dbk:listitem) = dbk:listitem * - + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject * - + dbk:remark (dbk:remark) = dbk:remark * - - dbk:continuation (String) - - dbk:inheritnum (String) - - dbk:numeration (String) - - dbk:spacing (String) - - dbk:startingnumber (String) - -[dbk:orgdiv] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:markupInlines, argeodbk:publishingInlines, argeodbk:techDocInlines, argeodbk:ubiquitousInlines - -[dbk:orgname] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines - - dbk:class (String) - - dbk:otherclass (String) - -[dbk:para] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:markupInlines, argeodbk:publishingElements, argeodbk:publishingInlines, argeodbk:techDocElements, argeodbk:techDocInlines, argeodbk:ubiquitousInlines - + dbk:info (dbk:info) = dbk:info * - + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject * - -[dbk:personblurb] > argeodbk:base, argeodbk:linkingAttributes, argeodbk:paragraphElements, argeodbk:titled - + dbk:anchor (dbk:anchor) = dbk:anchor * - -[dbk:personname] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines - -[dbk:phrase] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:markupInlines, argeodbk:publishingInlines, argeodbk:techDocInlines, argeodbk:ubiquitousInlines - -[dbk:remark] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines - -[dbk:replaceable] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines - - dbk:class (String) - -[dbk:row] > argeodbk:base, argeodbk:linkingAttributes - + dbk:entry (dbk:entry) = dbk:entry * - + dbk:entrytbl (dbk:entrytbl) = dbk:entrytbl * - - dbk:rowsep (String) - - dbk:valign (String) - -[dbk:section] > argeodbk:abstractSection, argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled - + dbk:anchor (dbk:anchor) = dbk:anchor * - + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject * - + dbk:section (dbk:section) = dbk:section * - -[dbk:set] > argeodbk:base, argeodbk:linkingAttributes, argeodbk:titled - + dbk:book (dbk:book) = dbk:book * - + dbk:set (dbk:set) = dbk:set * - + dbk:subtitle (dbk:subtitle) = dbk:subtitle * - - dbk:label (String) - - dbk:status (String) - -[dbk:simplelist] > argeodbk:base, argeodbk:linkingAttributes - - dbk:columns (String) - - dbk:type (String) - -[dbk:spanspec] > argeodbk:base, argeodbk:linkingAttributes - - dbk:align (String) - - dbk:char (String) - - dbk:charoff (String) - - dbk:colsep (String) - - dbk:nameend (String) - - dbk:namest (String) - - dbk:rowsep (String) - - dbk:spanname (String) - -[dbk:subscript] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines - -[dbk:subtitle] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:markupInlines, argeodbk:publishingInlines, argeodbk:techDocInlines, argeodbk:ubiquitousInlines - -[dbk:superscript] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines - -[dbk:table] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:titled - + dbk:caption (dbk:caption) = dbk:caption - + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject * - + dbk:tbody (dbk:tbody) = dbk:tbody * - + dbk:textobject (dbk:textobject) = dbk:textobject * - + dbk:tfoot (dbk:tfoot) = dbk:tfoot - + dbk:tgroup (dbk:tgroup) = dbk:tgroup * - + dbk:thead (dbk:thead) = dbk:thead - - dbk:border (String) - - dbk:cellpadding (String) - - dbk:cellspacing (String) - - dbk:class (String) - - dbk:colsep (String) - - dbk:floatstyle (String) - - dbk:frame (String) - - dbk:label (String) - - dbk:lang (String) - - dbk:onclick (String) - - dbk:ondblclick (String) - - dbk:onkeydown (String) - - dbk:onkeypress (String) - - dbk:onkeyup (String) - - dbk:onmousedown (String) - - dbk:onmousemove (String) - - dbk:onmouseout (String) - - dbk:onmouseover (String) - - dbk:onmouseup (String) - - dbk:orient (String) - - dbk:pgwide (String) - - dbk:rowheader (String) - - dbk:rowsep (String) - - dbk:rules (String) - - dbk:shortentry (String) - - dbk:style (String) - - dbk:summary (String) - - dbk:tabstyle (String) - - dbk:title (String) - - dbk:tocentry (String) - - dbk:width (String) - -[dbk:tbody] > argeodbk:base, argeodbk:linkingAttributes - + dbk:row (dbk:row) = dbk:row * - - dbk:align (String) - - dbk:char (String) - - dbk:charoff (String) - - dbk:class (String) - - dbk:lang (String) - - dbk:onclick (String) - - dbk:ondblclick (String) - - dbk:onkeydown (String) - - dbk:onkeypress (String) - - dbk:onkeyup (String) - - dbk:onmousedown (String) - - dbk:onmousemove (String) - - dbk:onmouseout (String) - - dbk:onmouseover (String) - - dbk:onmouseup (String) - - dbk:style (String) - - dbk:title (String) - - dbk:valign (String) - -[dbk:textobject] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements - + dbk:anchor (dbk:anchor) = dbk:anchor * - + dbk:annotation (dbk:annotation) = dbk:annotation * - + dbk:info (dbk:info) = dbk:info - + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject * - + dbk:phrase (dbk:phrase) = dbk:phrase - + dbk:remark (dbk:remark) = dbk:remark * - -[dbk:tfoot] > argeodbk:base, argeodbk:linkingAttributes - + dbk:colspec (dbk:colspec) = dbk:colspec * - + dbk:row (dbk:row) = dbk:row * - - dbk:align (String) - - dbk:char (String) - - dbk:charoff (String) - - dbk:class (String) - - dbk:lang (String) - - dbk:onclick (String) - - dbk:ondblclick (String) - - dbk:onkeydown (String) - - dbk:onkeypress (String) - - dbk:onkeyup (String) - - dbk:onmousedown (String) - - dbk:onmousemove (String) - - dbk:onmouseout (String) - - dbk:onmouseover (String) - - dbk:onmouseup (String) - - dbk:style (String) - - dbk:title (String) - - dbk:valign (String) - -[dbk:tgroup] > argeodbk:base, argeodbk:linkingAttributes - + dbk:colspec (dbk:colspec) = dbk:colspec * - + dbk:spanspec (dbk:spanspec) = dbk:spanspec * - + dbk:tbody (dbk:tbody) = dbk:tbody - + dbk:tfoot (dbk:tfoot) = dbk:tfoot - + dbk:thead (dbk:thead) = dbk:thead - - dbk:align (String) - - dbk:char (String) - - dbk:charoff (String) - - dbk:cols (String) - - dbk:colsep (String) - - dbk:rowsep (String) - - dbk:tgroupstyle (String) - -[dbk:thead] > argeodbk:base, argeodbk:linkingAttributes - + dbk:colspec (dbk:colspec) = dbk:colspec * - + dbk:row (dbk:row) = dbk:row * - - dbk:align (String) - - dbk:char (String) - - dbk:charoff (String) - - dbk:class (String) - - dbk:lang (String) - - dbk:onclick (String) - - dbk:ondblclick (String) - - dbk:onkeydown (String) - - dbk:onkeypress (String) - - dbk:onkeyup (String) - - dbk:onmousedown (String) - - dbk:onmousemove (String) - - dbk:onmouseout (String) - - dbk:onmouseover (String) - - dbk:onmouseup (String) - - dbk:style (String) - - dbk:title (String) - - dbk:valign (String) - -[dbk:title] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:markupInlines, argeodbk:publishingInlines, argeodbk:techDocInlines, argeodbk:ubiquitousInlines - -[dbk:videodata] > argeodbk:base - + dbk:info (dbk:info) = dbk:info - - dbk:align (String) - - dbk:contentdepth (String) - - dbk:contentwidth (String) - - dbk:depth (String) - - dbk:entityref (String) - - dbk:fileref (String) - - dbk:format (String) - - dbk:scale (String) - - dbk:scalefit (String) - - dbk:valign (String) - - dbk:width (String) - -[dbk:videoobject] > argeodbk:base, argeodbk:linkingAttributes - + dbk:info (dbk:info) = dbk:info - + dbk:videodata (dbk:videodata) = dbk:videodata - -[dbk:xmltext] > nt:base - - jcr:xmlcharacters (String) - -[dbk:xref] > argeodbk:base, argeodbk:linkingAttributes - - dbk:endterm (Reference) - - dbk:xrefstyle (String) - - diff --git a/org.argeo.server.jcr/src/org/argeo/jcr/fs/JcrFileStore.java b/org.argeo.server.jcr/src/org/argeo/jcr/fs/JcrFileStore.java deleted file mode 100644 index 32a3ecb5f..000000000 --- a/org.argeo.server.jcr/src/org/argeo/jcr/fs/JcrFileStore.java +++ /dev/null @@ -1,72 +0,0 @@ -package org.argeo.jcr.fs; - -import java.io.IOException; -import java.nio.file.FileStore; -import java.nio.file.attribute.FileAttributeView; -import java.nio.file.attribute.FileStoreAttributeView; - -public class JcrFileStore extends FileStore { - - @Override - public String name() { - // TODO Auto-generated method stub - return null; - } - - @Override - public String type() { - // TODO Auto-generated method stub - return null; - } - - @Override - public boolean isReadOnly() { - // TODO Auto-generated method stub - return false; - } - - @Override - public long getTotalSpace() throws IOException { - // TODO Auto-generated method stub - return 0; - } - - @Override - public long getUsableSpace() throws IOException { - // TODO Auto-generated method stub - return 0; - } - - @Override - public long getUnallocatedSpace() throws IOException { - // TODO Auto-generated method stub - return 0; - } - - @Override - public boolean supportsFileAttributeView( - Class type) { - // TODO Auto-generated method stub - return false; - } - - @Override - public boolean supportsFileAttributeView(String name) { - // TODO Auto-generated method stub - return false; - } - - @Override - public V getFileStoreAttributeView( - Class type) { - // TODO Auto-generated method stub - return null; - } - - @Override - public Object getAttribute(String attribute) throws IOException { - // TODO Auto-generated method stub - return null; - } - -} diff --git a/org.argeo.server.jcr/src/org/argeo/jcr/fs/JcrFileSystem.java b/org.argeo.server.jcr/src/org/argeo/jcr/fs/JcrFileSystem.java deleted file mode 100644 index 40328e8a0..000000000 --- a/org.argeo.server.jcr/src/org/argeo/jcr/fs/JcrFileSystem.java +++ /dev/null @@ -1,97 +0,0 @@ -package org.argeo.jcr.fs; - -import java.io.IOException; -import java.nio.file.FileStore; -import java.nio.file.FileSystem; -import java.nio.file.Path; -import java.nio.file.PathMatcher; -import java.nio.file.WatchService; -import java.nio.file.attribute.UserPrincipalLookupService; -import java.nio.file.spi.FileSystemProvider; -import java.util.Set; - -import javax.jcr.Session; - -import org.argeo.jcr.JcrUtils; - -public class JcrFileSystem extends FileSystem { - private final JcrFileSystemProvider provider; - private final Session session; - - public JcrFileSystem(JcrFileSystemProvider provider, Session session) { - super(); - this.provider = provider; - this.session = session; - } - - @Override - public FileSystemProvider provider() { - return provider; - } - - @Override - public void close() throws IOException { - JcrUtils.logoutQuietly(session); - } - - @Override - public boolean isOpen() { - return session.isLive(); - } - - @Override - public boolean isReadOnly() { - return false; - } - - @Override - public String getSeparator() { - return "/"; - } - - @Override - public Iterable getRootDirectories() { - return null; - } - - @Override - public Iterable getFileStores() { - // TODO Auto-generated method stub - return null; - } - - @Override - public Set supportedFileAttributeViews() { - // TODO Auto-generated method stub - return null; - } - - @Override - public Path getPath(String first, String... more) { - // TODO Auto-generated method stub - return null; - } - - @Override - public PathMatcher getPathMatcher(String syntaxAndPattern) { - // TODO Auto-generated method stub - return null; - } - - @Override - public UserPrincipalLookupService getUserPrincipalLookupService() { - // TODO Auto-generated method stub - return null; - } - - @Override - public WatchService newWatchService() throws IOException { - // TODO Auto-generated method stub - return null; - } - - public Session getSession() { - return session; - } - -} diff --git a/org.argeo.server.jcr/src/org/argeo/jcr/fs/JcrFileSystemProvider.java b/org.argeo.server.jcr/src/org/argeo/jcr/fs/JcrFileSystemProvider.java deleted file mode 100644 index 8ea4cca2e..000000000 --- a/org.argeo.server.jcr/src/org/argeo/jcr/fs/JcrFileSystemProvider.java +++ /dev/null @@ -1,142 +0,0 @@ -package org.argeo.jcr.fs; - -import java.io.IOException; -import java.net.URI; -import java.nio.channels.SeekableByteChannel; -import java.nio.file.AccessMode; -import java.nio.file.CopyOption; -import java.nio.file.DirectoryStream; -import java.nio.file.DirectoryStream.Filter; -import java.nio.file.FileStore; -import java.nio.file.FileSystem; -import java.nio.file.LinkOption; -import java.nio.file.OpenOption; -import java.nio.file.Path; -import java.nio.file.attribute.BasicFileAttributes; -import java.nio.file.attribute.FileAttribute; -import java.nio.file.attribute.FileAttributeView; -import java.nio.file.spi.FileSystemProvider; -import java.util.Map; -import java.util.Set; - -public class JcrFileSystemProvider extends FileSystemProvider { - - @Override - public String getScheme() { - return "jcr"; - } - - @Override - public FileSystem newFileSystem(URI uri, Map env) - throws IOException { - // TODO Auto-generated method stub - return null; - } - - @Override - public FileSystem getFileSystem(URI uri) { - // TODO Auto-generated method stub - return null; - } - - @Override - public Path getPath(URI uri) { - // TODO Auto-generated method stub - return null; - } - - @Override - public SeekableByteChannel newByteChannel(Path path, - Set options, FileAttribute... attrs) - throws IOException { - // TODO Auto-generated method stub - return null; - } - - @Override - public DirectoryStream newDirectoryStream(Path dir, - Filter filter) throws IOException { - // TODO Auto-generated method stub - return null; - } - - @Override - public void createDirectory(Path dir, FileAttribute... attrs) - throws IOException { - // TODO Auto-generated method stub - - } - - @Override - public void delete(Path path) throws IOException { - // TODO Auto-generated method stub - - } - - @Override - public void copy(Path source, Path target, CopyOption... options) - throws IOException { - // TODO Auto-generated method stub - - } - - @Override - public void move(Path source, Path target, CopyOption... options) - throws IOException { - // TODO Auto-generated method stub - - } - - @Override - public boolean isSameFile(Path path, Path path2) throws IOException { - // TODO Auto-generated method stub - return false; - } - - @Override - public boolean isHidden(Path path) throws IOException { - // TODO Auto-generated method stub - return false; - } - - @Override - public FileStore getFileStore(Path path) throws IOException { - // TODO Auto-generated method stub - return null; - } - - @Override - public void checkAccess(Path path, AccessMode... modes) throws IOException { - // TODO Auto-generated method stub - - } - - @Override - public V getFileAttributeView(Path path, - Class type, LinkOption... options) { - // TODO Auto-generated method stub - return null; - } - - @Override - public A readAttributes(Path path, - Class type, LinkOption... options) throws IOException { - // TODO Auto-generated method stub - return null; - } - - @Override - public Map readAttributes(Path path, String attributes, - LinkOption... options) throws IOException { - // TODO Auto-generated method stub - return null; - } - - @Override - public void setAttribute(Path path, String attribute, Object value, - LinkOption... options) throws IOException { - // TODO Auto-generated method stub - - } - -} diff --git a/org.argeo.server.jcr/src/org/argeo/jcr/fs/JcrFsException.java b/org.argeo.server.jcr/src/org/argeo/jcr/fs/JcrFsException.java deleted file mode 100644 index f214fdc44..000000000 --- a/org.argeo.server.jcr/src/org/argeo/jcr/fs/JcrFsException.java +++ /dev/null @@ -1,15 +0,0 @@ -package org.argeo.jcr.fs; - - -/** Exception related to the JCR FS */ -public class JcrFsException extends RuntimeException { - private static final long serialVersionUID = -7973896038244922980L; - - public JcrFsException(String message, Throwable e) { - super(message, e); - } - - public JcrFsException(String message) { - super(message); - } -} diff --git a/org.argeo.server.jcr/src/org/argeo/jcr/fs/JcrPath.java b/org.argeo.server.jcr/src/org/argeo/jcr/fs/JcrPath.java deleted file mode 100644 index e25293517..000000000 --- a/org.argeo.server.jcr/src/org/argeo/jcr/fs/JcrPath.java +++ /dev/null @@ -1,199 +0,0 @@ -package org.argeo.jcr.fs; - -import java.io.File; -import java.io.IOException; -import java.net.URI; -import java.nio.file.FileSystem; -import java.nio.file.LinkOption; -import java.nio.file.Path; -import java.nio.file.WatchEvent.Kind; -import java.nio.file.WatchEvent.Modifier; -import java.nio.file.WatchKey; -import java.nio.file.WatchService; -import java.util.Iterator; - -import javax.jcr.Node; -import javax.jcr.RepositoryException; - -public class JcrPath implements Path { - private JcrFileSystem filesSystem; - private String path; - - private Node node; - - public JcrPath(JcrFileSystem filesSystem, Node node) { - super(); - this.filesSystem = filesSystem; - this.node = node; - } - - @Override - public FileSystem getFileSystem() { - return filesSystem; - } - - @Override - public boolean isAbsolute() { - return path.startsWith("/"); - } - - @Override - public Path getRoot() { - try { - return new JcrPath(filesSystem, node.getSession().getRootNode()); - } catch (RepositoryException e) { - throw new JcrFsException("Cannot get root", e); - } - } - - @Override - public Path getFileName() { - return null; - } - - @Override - public Path getParent() { - // TODO Auto-generated method stub - return null; - } - - @Override - public int getNameCount() { - // TODO Auto-generated method stub - return 0; - } - - @Override - public Path getName(int index) { - // TODO Auto-generated method stub - return null; - } - - @Override - public Path subpath(int beginIndex, int endIndex) { - // TODO Auto-generated method stub - return null; - } - - @Override - public boolean startsWith(Path other) { - // TODO Auto-generated method stub - return false; - } - - @Override - public boolean startsWith(String other) { - // TODO Auto-generated method stub - return false; - } - - @Override - public boolean endsWith(Path other) { - // TODO Auto-generated method stub - return false; - } - - @Override - public boolean endsWith(String other) { - // TODO Auto-generated method stub - return false; - } - - @Override - public Path normalize() { - // TODO Auto-generated method stub - return null; - } - - @Override - public Path resolve(Path other) { - // TODO Auto-generated method stub - return null; - } - - @Override - public Path resolve(String other) { - // TODO Auto-generated method stub - return null; - } - - @Override - public Path resolveSibling(Path other) { - // TODO Auto-generated method stub - return null; - } - - @Override - public Path resolveSibling(String other) { - // TODO Auto-generated method stub - return null; - } - - @Override - public Path relativize(Path other) { - // TODO Auto-generated method stub - return null; - } - - @Override - public URI toUri() { - // TODO Auto-generated method stub - return null; - } - - @Override - public Path toAbsolutePath() { - // TODO Auto-generated method stub - return null; - } - - @Override - public Path toRealPath(LinkOption... options) throws IOException { - // TODO Auto-generated method stub - return null; - } - - @Override - public File toFile() { - throw new UnsupportedOperationException(); - } - - @Override - public WatchKey register(WatchService watcher, Kind[] events, - Modifier... modifiers) throws IOException { - // TODO Auto-generated method stub - return null; - } - - @Override - public WatchKey register(WatchService watcher, Kind... events) - throws IOException { - // TODO Auto-generated method stub - return null; - } - - @Override - public Iterator iterator() { - // TODO Auto-generated method stub - return null; - } - - @Override - public int compareTo(Path other) { - // TODO Auto-generated method stub - return 0; - } - - public Node getNode() { - if (!isAbsolute())// TODO default dir - throw new JcrFsException("Cannot get node from relative path"); - try { - if (node == null) - node = filesSystem.getSession().getNode(path); - return node; - } catch (RepositoryException e) { - throw new JcrFsException("Cannot get node", e); - } - } - -} diff --git a/org.argeo.server.jcr/src/org/argeo/jcr/proxy/AbstractUrlProxy.java b/org.argeo.server.jcr/src/org/argeo/jcr/proxy/AbstractUrlProxy.java deleted file mode 100644 index 30369ce7d..000000000 --- a/org.argeo.server.jcr/src/org/argeo/jcr/proxy/AbstractUrlProxy.java +++ /dev/null @@ -1,178 +0,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. - */ -package org.argeo.jcr.proxy; - -import java.io.IOException; -import java.io.InputStream; -import java.net.URL; - -import javax.jcr.Binary; -import javax.jcr.Node; -import javax.jcr.Property; -import javax.jcr.Repository; -import javax.jcr.RepositoryException; -import javax.jcr.Session; -import javax.jcr.nodetype.NodeType; - -import org.apache.commons.io.IOUtils; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.argeo.jcr.ArgeoJcrException; -import org.argeo.jcr.JcrUtils; - -/** Base class for URL based proxys. */ -public abstract class AbstractUrlProxy implements ResourceProxy { - private final static Log log = LogFactory.getLog(AbstractUrlProxy.class); - - private Repository jcrRepository; - private Session jcrAdminSession; - private String proxyWorkspace = "proxy"; - - protected abstract Node retrieve(Session session, String path); - - void init() { - try { - jcrAdminSession = JcrUtils.loginOrCreateWorkspace(jcrRepository, - proxyWorkspace); - beforeInitSessionSave(jcrAdminSession); - if (jcrAdminSession.hasPendingChanges()) - jcrAdminSession.save(); - } catch (Exception e) { - JcrUtils.discardQuietly(jcrAdminSession); - throw new ArgeoJcrException("Cannot initialize Maven proxy", e); - } - } - - /** - * Called before the (admin) session is saved at the end of the - * initialization. Does nothing by default, to be overridden. - */ - protected void beforeInitSessionSave(Session session) - throws RepositoryException { - } - - void destroy() { - JcrUtils.logoutQuietly(jcrAdminSession); - } - - /** - * Called before the (admin) session is logged out when resources are - * released. Does nothing by default, to be overridden. - */ - protected void beforeDestroySessionLogout() throws RepositoryException { - } - - public Node proxy(String path) { - // we open a JCR session with client credentials in order not to use the - // admin session in multiple thread or make it a bottleneck. - Node nodeAdmin = null; - Node nodeClient = null; - Session clientSession = null; - try { - clientSession = jcrRepository.login(proxyWorkspace); - if (!clientSession.itemExists(path) - || shouldUpdate(clientSession, path)) { - nodeAdmin = retrieveAndSave(path); - if (nodeAdmin != null) - nodeClient = clientSession.getNode(path); - } else - nodeClient = clientSession.getNode(path); - return nodeClient; - } catch (RepositoryException e) { - throw new ArgeoJcrException("Cannot proxy " + path, e); - } finally { - if (nodeClient == null) - JcrUtils.logoutQuietly(clientSession); - } - } - - protected synchronized Node retrieveAndSave(String path) { - try { - Node node = retrieve(jcrAdminSession, path); - if (node == null) - return null; - jcrAdminSession.save(); - return node; - } catch (RepositoryException e) { - JcrUtils.discardQuietly(jcrAdminSession); - throw new ArgeoJcrException("Cannot retrieve and save " + path, e); - } finally { - notifyAll(); - } - } - - /** Session is not saved */ - protected synchronized Node proxyUrl(Session session, String remoteUrl, - String path) throws RepositoryException { - Node node = null; - if (session.itemExists(path)) { - // throw new ArgeoJcrException("Node " + path + " already exists"); - } - InputStream in = null; - try { - URL u = new URL(remoteUrl); - in = u.openStream(); - node = importFile(session, path, in); - } catch (IOException e) { - if (log.isDebugEnabled()) { - log.debug("Cannot read " + remoteUrl + ", skipping... " - + e.getMessage()); - // log.trace("Cannot read because of ", e); - } - JcrUtils.discardQuietly(session); - } finally { - IOUtils.closeQuietly(in); - } - return node; - } - - protected synchronized Node importFile(Session session, String path, - InputStream in) throws RepositoryException { - Binary binary = null; - try { - Node content = null; - Node node = null; - if (!session.itemExists(path)) { - node = JcrUtils.mkdirs(session, path, NodeType.NT_FILE, - NodeType.NT_FOLDER, false); - content = node.addNode(Node.JCR_CONTENT, NodeType.NT_RESOURCE); - } else { - node = session.getNode(path); - content = node.getNode(Node.JCR_CONTENT); - } - binary = session.getValueFactory().createBinary(in); - content.setProperty(Property.JCR_DATA, binary); - JcrUtils.updateLastModifiedAndParents(node, null); - return node; - } finally { - JcrUtils.closeQuietly(binary); - } - } - - /** Whether the file should be updated. */ - protected Boolean shouldUpdate(Session clientSession, String nodePath) { - return false; - } - - public void setJcrRepository(Repository jcrRepository) { - this.jcrRepository = jcrRepository; - } - - public void setProxyWorkspace(String localWorkspace) { - this.proxyWorkspace = localWorkspace; - } - -} diff --git a/org.argeo.server.jcr/src/org/argeo/jcr/proxy/ResourceProxy.java b/org.argeo.server.jcr/src/org/argeo/jcr/proxy/ResourceProxy.java deleted file mode 100644 index b4fb33286..000000000 --- a/org.argeo.server.jcr/src/org/argeo/jcr/proxy/ResourceProxy.java +++ /dev/null @@ -1,31 +0,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. - */ -package org.argeo.jcr.proxy; - -import javax.jcr.Node; - -/** A proxy which nows how to resolve and synchronize relative URLs */ -public interface ResourceProxy { - /** - * Proxy the file referenced by this relative path in the underlying - * repository. A new session is created by each call, so the underlying - * session of the returned node must be closed by the caller. - * - * @return the proxied Node, null if the resource was not found - * (e.g. HTTP 404) - */ - public Node proxy(String relativePath); -} diff --git a/org.argeo.server.jcr/src/org/argeo/jcr/proxy/ResourceProxyServlet.java b/org.argeo.server.jcr/src/org/argeo/jcr/proxy/ResourceProxyServlet.java deleted file mode 100644 index e92e2a402..000000000 --- a/org.argeo.server.jcr/src/org/argeo/jcr/proxy/ResourceProxyServlet.java +++ /dev/null @@ -1,140 +0,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. - */ -package org.argeo.jcr.proxy; - -import java.io.IOException; -import java.io.InputStream; - -import javax.jcr.Binary; -import javax.jcr.Node; -import javax.jcr.PathNotFoundException; -import javax.jcr.Property; -import javax.jcr.RepositoryException; -import javax.servlet.ServletException; -import javax.servlet.http.HttpServlet; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -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.argeo.jcr.ArgeoJcrException; -import org.argeo.jcr.JcrUtils; - -/** Wraps a proxy via HTTP */ -public class ResourceProxyServlet extends HttpServlet { - private static final long serialVersionUID = -8886549549223155801L; - - private final static Log log = LogFactory - .getLog(ResourceProxyServlet.class); - - private ResourceProxy proxy; - - private String contentTypeCharset = "UTF-8"; - - @Override - protected void doGet(HttpServletRequest request, - HttpServletResponse response) throws ServletException, IOException { - String path = request.getPathInfo(); - - if (log.isTraceEnabled()) { - log.trace("path=" + path); - log.trace("UserPrincipal = " + request.getUserPrincipal().getName()); - log.trace("SessionID = " + request.getSession().getId()); - log.trace("ContextPath = " + request.getContextPath()); - log.trace("ServletPath = " + request.getServletPath()); - log.trace("PathInfo = " + request.getPathInfo()); - log.trace("Method = " + request.getMethod()); - log.trace("User-Agent = " + request.getHeader("User-Agent")); - } - - Node node = null; - try { - node = proxy.proxy(path); - if (node == null) - response.sendError(404); - else - processResponse(node, response); - } finally { - if (node != null) - try { - JcrUtils.logoutQuietly(node.getSession()); - } catch (RepositoryException e) { - // silent - } - } - - } - - /** Retrieve the content of the node. */ - protected void processResponse(Node node, HttpServletResponse response) { - Binary binary = null; - InputStream in = null; - try { - String fileName = node.getName(); - String ext = FilenameUtils.getExtension(fileName); - - // TODO use a more generic / standard approach - // see http://svn.apache.org/viewvc/tomcat/trunk/conf/web.xml - String contentType; - if ("xml".equals(ext)) - contentType = "text/xml;charset=" + contentTypeCharset; - else if ("jar".equals(ext)) - contentType = "application/java-archive"; - else if ("zip".equals(ext)) - contentType = "application/zip"; - else if ("gz".equals(ext)) - contentType = "application/x-gzip"; - else if ("bz2".equals(ext)) - contentType = "application/x-bzip2"; - else if ("tar".equals(ext)) - contentType = "application/x-tar"; - else if ("rpm".equals(ext)) - contentType = "application/x-redhat-package-manager"; - else - contentType = "application/octet-stream"; - contentType = contentType + ";name=\"" + fileName + "\""; - response.setHeader("Content-Disposition", "attachment; filename=\"" - + fileName + "\""); - response.setHeader("Expires", "0"); - response.setHeader("Cache-Control", "no-cache, must-revalidate"); - response.setHeader("Pragma", "no-cache"); - - response.setContentType(contentType); - - try { - binary = node.getNode(Property.JCR_CONTENT) - .getProperty(Property.JCR_DATA).getBinary(); - } catch (PathNotFoundException e) { - log.error("Node " + node + " as no data under content"); - throw e; - } - in = binary.getStream(); - IOUtils.copy(in, response.getOutputStream()); - } catch (Exception e) { - throw new ArgeoJcrException("Cannot download " + node, e); - } finally { - IOUtils.closeQuietly(in); - JcrUtils.closeQuietly(binary); - } - } - - public void setProxy(ResourceProxy resourceProxy) { - this.proxy = resourceProxy; - } - -} diff --git a/org.argeo.server.jcr/src/org/argeo/jcr/security/JcrAuthorizations.java b/org.argeo.server.jcr/src/org/argeo/jcr/security/JcrAuthorizations.java deleted file mode 100644 index 9ad249252..000000000 --- a/org.argeo.server.jcr/src/org/argeo/jcr/security/JcrAuthorizations.java +++ /dev/null @@ -1,223 +0,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. - */ -package org.argeo.jcr.security; - -import java.security.Principal; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import javax.jcr.Repository; -import javax.jcr.RepositoryException; -import javax.jcr.Session; -import javax.jcr.security.AccessControlManager; -import javax.jcr.security.Privilege; - -import org.argeo.jcr.ArgeoJcrException; -import org.argeo.jcr.JcrUtils; -import org.argeo.util.security.SimplePrincipal; - -/** Apply authorizations to a JCR repository. */ -public class JcrAuthorizations implements Runnable { - // private final static Log log = - // LogFactory.getLog(JcrAuthorizations.class); - - private Repository repository; - private String workspace = null; - - private String securityWorkspace = "security"; - - /** - * key := privilege1,privilege2/path/to/node
- * value := group1,group2,user1 - */ - private Map principalPrivileges = new HashMap(); - - public void run() { - String currentWorkspace = workspace; - Session session = null; - try { - if (workspace != null && workspace.equals("*")) { - session = repository.login(); - String[] workspaces = session.getWorkspace() - .getAccessibleWorkspaceNames(); - JcrUtils.logoutQuietly(session); - for (String wksp : workspaces) { - currentWorkspace = wksp; - if (currentWorkspace.equals(securityWorkspace)) - continue; - session = repository.login(currentWorkspace); - initAuthorizations(session); - JcrUtils.logoutQuietly(session); - } - } else { - session = repository.login(workspace); - initAuthorizations(session); - } - } catch (Exception e) { - JcrUtils.discardQuietly(session); - throw new ArgeoJcrException( - "Cannot set authorizations " + principalPrivileges - + " on workspace " + currentWorkspace, e); - } finally { - JcrUtils.logoutQuietly(session); - } - } - - protected void processWorkspace(String workspace) { - Session session = null; - try { - session = repository.login(workspace); - initAuthorizations(session); - } catch (Exception e) { - JcrUtils.discardQuietly(session); - throw new ArgeoJcrException("Cannot set authorizations " - + principalPrivileges + " on repository " + repository, e); - } finally { - JcrUtils.logoutQuietly(session); - } - } - - /** @deprecated call {@link #run()} instead. */ - @Deprecated - public void init() { - run(); - } - - protected void initAuthorizations(Session session) - throws RepositoryException { - AccessControlManager acm = session.getAccessControlManager(); - - for (String privileges : principalPrivileges.keySet()) { - String path = null; - int slashIndex = privileges.indexOf('/'); - if (slashIndex == 0) { - throw new ArgeoJcrException("Privilege " + privileges - + " badly formatted it starts with /"); - } else if (slashIndex > 0) { - path = privileges.substring(slashIndex); - privileges = privileges.substring(0, slashIndex); - } - - if (path == null) - path = "/"; - - List privs = new ArrayList(); - for (String priv : privileges.split(",")) { - privs.add(acm.privilegeFromName(priv)); - } - - String principalNames = principalPrivileges.get(privileges); - for (String principalName : principalNames.split(",")) { - Principal principal = getOrCreatePrincipal(session, - principalName); - JcrUtils.addPrivileges(session, path, principal, privs); - // if (log.isDebugEnabled()) { - // StringBuffer privBuf = new StringBuffer(); - // for (Privilege priv : privs) - // privBuf.append(priv.getName()); - // log.debug("Added privileges " + privBuf + " to " - // + principal.getName() + " on " + path + " in '" - // + session.getWorkspace().getName() + "'"); - // } - } - } - - // if (log.isDebugEnabled()) - // log.debug("JCR authorizations applied on '" - // + session.getWorkspace().getName() + "'"); - } - - /** - * Returns a {@link SimplePrincipal}, does not check whether it exists since - * such capabilities is not provided by the standard JCR API. Can be - * overridden to provide smarter handling - */ - protected Principal getOrCreatePrincipal(Session session, - String principalName) throws RepositoryException { - return new SimplePrincipal(principalName); - } - - // public static void addPrivileges(Session session, Principal principal, - // String path, List privs) throws RepositoryException { - // AccessControlManager acm = session.getAccessControlManager(); - // // search for an access control list - // AccessControlList acl = null; - // AccessControlPolicyIterator policyIterator = acm - // .getApplicablePolicies(path); - // if (policyIterator.hasNext()) { - // while (policyIterator.hasNext()) { - // AccessControlPolicy acp = policyIterator - // .nextAccessControlPolicy(); - // if (acp instanceof AccessControlList) - // acl = ((AccessControlList) acp); - // } - // } else { - // AccessControlPolicy[] existingPolicies = acm.getPolicies(path); - // for (AccessControlPolicy acp : existingPolicies) { - // if (acp instanceof AccessControlList) - // acl = ((AccessControlList) acp); - // } - // } - // - // if (acl != null) { - // acl.addAccessControlEntry(principal, - // privs.toArray(new Privilege[privs.size()])); - // acm.setPolicy(path, acl); - // session.save(); - // if (log.isDebugEnabled()) { - // StringBuffer buf = new StringBuffer(""); - // for (int i = 0; i < privs.size(); i++) { - // if (i != 0) - // buf.append(','); - // buf.append(privs.get(i).getName()); - // } - // log.debug("Added privilege(s) '" + buf + "' to '" - // + principal.getName() + "' on " + path - // + " from workspace '" - // + session.getWorkspace().getName() + "'"); - // } - // } else { - // throw new ArgeoJcrException("Don't know how to apply privileges " - // + privs + " to " + principal + " on " + path - // + " from workspace '" + session.getWorkspace().getName() - // + "'"); - // } - // } - - @Deprecated - public void setGroupPrivileges(Map groupPrivileges) { - this.principalPrivileges = groupPrivileges; - } - - public void setPrincipalPrivileges(Map principalPrivileges) { - this.principalPrivileges = principalPrivileges; - } - - public void setRepository(Repository repository) { - this.repository = repository; - } - - public void setWorkspace(String workspace) { - this.workspace = workspace; - } - - public void setSecurityWorkspace(String securityWorkspace) { - this.securityWorkspace = securityWorkspace; - } - -} diff --git a/org.argeo.server.jcr/src/org/argeo/jcr/security/JcrKeyring.java b/org.argeo.server.jcr/src/org/argeo/jcr/security/JcrKeyring.java deleted file mode 100644 index 8ab6ed355..000000000 --- a/org.argeo.server.jcr/src/org/argeo/jcr/security/JcrKeyring.java +++ /dev/null @@ -1,295 +0,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. - */ -package org.argeo.jcr.security; - -import java.io.ByteArrayInputStream; -import java.io.CharArrayReader; -import java.io.InputStream; -import java.io.Reader; -import java.security.Provider; -import java.security.SecureRandom; - -import javax.crypto.Cipher; -import javax.crypto.CipherInputStream; -import javax.crypto.SecretKey; -import javax.crypto.spec.IvParameterSpec; -import javax.jcr.Binary; -import javax.jcr.Node; -import javax.jcr.Property; -import javax.jcr.RepositoryException; -import javax.jcr.Session; - -import org.apache.commons.io.IOUtils; -import org.argeo.jcr.ArgeoJcrException; -import org.argeo.jcr.ArgeoNames; -import org.argeo.jcr.ArgeoTypes; -import org.argeo.jcr.JcrUtils; -import org.argeo.jcr.UserJcrUtils; -import org.argeo.util.security.AbstractKeyring; -import org.argeo.util.security.PBEKeySpecCallback; - -/** JCR based implementation of a keyring */ -public class JcrKeyring extends AbstractKeyring implements ArgeoNames { - /** - * Stronger with 256, but causes problem with Oracle JVM, force 128 in this - * case - */ - public final static Long DEFAULT_SECRETE_KEY_LENGTH = 256l; - public final static String DEFAULT_SECRETE_KEY_FACTORY = "PBKDF2WithHmacSHA1"; - public final static String DEFAULT_SECRETE_KEY_ENCRYPTION = "AES"; - public final static String DEFAULT_CIPHER_NAME = "AES/CBC/PKCS5Padding"; - - private Integer iterationCountFactor = 200; - private Long secreteKeyLength = DEFAULT_SECRETE_KEY_LENGTH; - private String secreteKeyFactoryName = DEFAULT_SECRETE_KEY_FACTORY; - private String secreteKeyEncryption = DEFAULT_SECRETE_KEY_ENCRYPTION; - private String cipherName = DEFAULT_CIPHER_NAME; - - private Session session; - - /** - * When setup is called the session has not yet been saved and we don't want - * to save it since there maybe other data which would be inconsistent. So - * we keep a reference to this node which will then be used (an reset to - * null) when handling the PBE callback. We keep one per thread in case - * multiple users are accessing the same instance of a keyring. - */ - private ThreadLocal notYetSavedKeyring = new ThreadLocal() { - - @Override - protected Node initialValue() { - return null; - } - }; - - @Override - protected Boolean isSetup() { - try { - if (notYetSavedKeyring.get() != null) - return true; - - Node userHome = UserJcrUtils.getUserHome(session); - return userHome.hasNode(ARGEO_KEYRING); - } catch (RepositoryException e) { - throw new ArgeoJcrException("Cannot check whether keyring is setup", e); - } - } - - @Override - protected void setup(char[] password) { - Binary binary = null; - InputStream in = null; - try { - Node userHome = UserJcrUtils.getUserHome(session); - if (userHome.hasNode(ARGEO_KEYRING)) - throw new ArgeoJcrException("Keyring already setup"); - Node keyring = userHome.addNode(ARGEO_KEYRING); - keyring.addMixin(ArgeoTypes.ARGEO_PBE_SPEC); - - // deterministic salt and iteration count based on username - String username = session.getUserID(); - byte[] salt = new byte[8]; - byte[] usernameBytes = username.getBytes(); - for (int i = 0; i < salt.length; i++) { - if (i < usernameBytes.length) - salt[i] = usernameBytes[i]; - else - salt[i] = 0; - } - in = new ByteArrayInputStream(salt); - binary = session.getValueFactory().createBinary(in); - keyring.setProperty(ARGEO_SALT, binary); - - Integer iterationCount = username.length() * iterationCountFactor; - keyring.setProperty(ARGEO_ITERATION_COUNT, iterationCount); - - // default algo - // TODO check if algo and key length are available, use DES if not - keyring.setProperty(ARGEO_SECRET_KEY_FACTORY, secreteKeyFactoryName); - keyring.setProperty(ARGEO_KEY_LENGTH, secreteKeyLength); - keyring.setProperty(ARGEO_SECRET_KEY_ENCRYPTION, secreteKeyEncryption); - keyring.setProperty(ARGEO_CIPHER, cipherName); - - // keyring.getSession().save(); - - // encrypted password hash - // IOUtils.closeQuietly(in); - // JcrUtils.closeQuietly(binary); - // byte[] btPass = hash(password, salt, iterationCount); - // in = new ByteArrayInputStream(btPass); - // binary = session.getValueFactory().createBinary(in); - // keyring.setProperty(ARGEO_PASSWORD, binary); - - notYetSavedKeyring.set(keyring); - } catch (Exception e) { - throw new ArgeoJcrException("Cannot setup keyring", e); - } finally { - JcrUtils.closeQuietly(binary); - IOUtils.closeQuietly(in); - // JcrUtils.discardQuietly(session); - } - } - - @Override - protected void handleKeySpecCallback(PBEKeySpecCallback pbeCallback) { - try { - Node userHome = UserJcrUtils.getUserHome(session); - Node keyring; - if (userHome.hasNode(ARGEO_KEYRING)) - keyring = userHome.getNode(ARGEO_KEYRING); - else if (notYetSavedKeyring.get() != null) - keyring = notYetSavedKeyring.get(); - else - throw new ArgeoJcrException("Keyring not setup"); - - pbeCallback.set(keyring.getProperty(ARGEO_SECRET_KEY_FACTORY).getString(), - JcrUtils.getBinaryAsBytes(keyring.getProperty(ARGEO_SALT)), - (int) keyring.getProperty(ARGEO_ITERATION_COUNT).getLong(), - (int) keyring.getProperty(ARGEO_KEY_LENGTH).getLong(), - keyring.getProperty(ARGEO_SECRET_KEY_ENCRYPTION).getString()); - - if (notYetSavedKeyring.get() != null) - notYetSavedKeyring.remove(); - } catch (RepositoryException e) { - throw new ArgeoJcrException("Cannot handle key spec callback", e); - } - } - - /** The parent node must already exist at this path. */ - @Override - protected synchronized void encrypt(String path, InputStream unencrypted) { - // should be called first for lazy initialization - SecretKey secretKey = getSecretKey(); - - Binary binary = null; - InputStream in = null; - try { - Cipher cipher = createCipher(); - Node node; - if (!session.nodeExists(path)) { - String parentPath = JcrUtils.parentPath(path); - if (!session.nodeExists(parentPath)) - throw new ArgeoJcrException("No parent node of " + path); - Node parentNode = session.getNode(parentPath); - node = parentNode.addNode(JcrUtils.nodeNameFromPath(path)); - } else { - node = session.getNode(path); - } - node.addMixin(ArgeoTypes.ARGEO_ENCRYPTED); - SecureRandom random = new SecureRandom(); - byte[] iv = new byte[16]; - random.nextBytes(iv); - cipher.init(Cipher.ENCRYPT_MODE, secretKey, new IvParameterSpec(iv)); - JcrUtils.setBinaryAsBytes(node, ARGEO_IV, iv); - - in = new CipherInputStream(unencrypted, cipher); - binary = session.getValueFactory().createBinary(in); - node.setProperty(Property.JCR_DATA, binary); - session.save(); - } catch (Exception e) { - throw new ArgeoJcrException("Cannot encrypt", e); - } finally { - IOUtils.closeQuietly(unencrypted); - IOUtils.closeQuietly(in); - JcrUtils.closeQuietly(binary); - } - } - - @Override - protected synchronized InputStream decrypt(String path) { - Binary binary = null; - InputStream encrypted = null; - Reader reader = null; - try { - if (!session.nodeExists(path)) { - char[] password = ask(); - reader = new CharArrayReader(password); - return new ByteArrayInputStream(IOUtils.toByteArray(reader)); - } else { - // should be called first for lazy initialisation - SecretKey secretKey = getSecretKey(); - - Cipher cipher = createCipher(); - - Node node = session.getNode(path); - if (node.hasProperty(ARGEO_IV)) { - byte[] iv = JcrUtils.getBinaryAsBytes(node.getProperty(ARGEO_IV)); - cipher.init(Cipher.DECRYPT_MODE, secretKey, new IvParameterSpec(iv)); - } else { - cipher.init(Cipher.DECRYPT_MODE, secretKey); - } - - binary = node.getProperty(Property.JCR_DATA).getBinary(); - encrypted = binary.getStream(); - return new CipherInputStream(encrypted, cipher); - } - } catch (Exception e) { - throw new ArgeoJcrException("Cannot decrypt", e); - } finally { - IOUtils.closeQuietly(encrypted); - IOUtils.closeQuietly(reader); - JcrUtils.closeQuietly(binary); - } - } - - protected Cipher createCipher() { - try { - Node userHome = UserJcrUtils.getUserHome(session); - if (!userHome.hasNode(ARGEO_KEYRING)) - throw new ArgeoJcrException("Keyring not setup"); - Node keyring = userHome.getNode(ARGEO_KEYRING); - String cipherName = keyring.getProperty(ARGEO_CIPHER).getString(); - Provider securityProvider = getSecurityProvider(); - Cipher cipher; - if (securityProvider == null)// TODO use BC? - cipher = Cipher.getInstance(cipherName); - else - cipher = Cipher.getInstance(cipherName, securityProvider); - return cipher; - } catch (Exception e) { - throw new ArgeoJcrException("Cannot get cipher", e); - } - } - - public synchronized void changePassword(char[] oldPassword, char[] newPassword) { - // TODO decrypt with old pw / encrypt with new pw all argeo:encrypted - } - - public synchronized void setSession(Session session) { - this.session = session; - } - - public void setIterationCountFactor(Integer iterationCountFactor) { - this.iterationCountFactor = iterationCountFactor; - } - - public void setSecreteKeyLength(Long keyLength) { - this.secreteKeyLength = keyLength; - } - - public void setSecreteKeyFactoryName(String secreteKeyFactoryName) { - this.secreteKeyFactoryName = secreteKeyFactoryName; - } - - public void setSecreteKeyEncryption(String secreteKeyEncryption) { - this.secreteKeyEncryption = secreteKeyEncryption; - } - - public void setCipherName(String cipherName) { - this.cipherName = cipherName; - } - -} \ No newline at end of file diff --git a/org.argeo.server.jcr/src/org/argeo/jcr/spring/ThreadBoundSession.java b/org.argeo.server.jcr/src/org/argeo/jcr/spring/ThreadBoundSession.java deleted file mode 100644 index 35f021555..000000000 --- a/org.argeo.server.jcr/src/org/argeo/jcr/spring/ThreadBoundSession.java +++ /dev/null @@ -1,34 +0,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. - */ -package org.argeo.jcr.spring; - -import org.argeo.jcr.ThreadBoundJcrSessionFactory; -import org.springframework.beans.factory.DisposableBean; -import org.springframework.beans.factory.FactoryBean; -import org.springframework.beans.factory.InitializingBean; - -@SuppressWarnings("rawtypes") -@Deprecated -public class ThreadBoundSession extends ThreadBoundJcrSessionFactory implements FactoryBean, InitializingBean, DisposableBean{ - public void afterPropertiesSet() throws Exception { - init(); - } - - public void destroy() throws Exception { - dispose(); - } - -} diff --git a/org.argeo.server.jcr/src/org/argeo/jcr/tabular/JcrTabularRowIterator.java b/org.argeo.server.jcr/src/org/argeo/jcr/tabular/JcrTabularRowIterator.java deleted file mode 100644 index d4ffbf8c2..000000000 --- a/org.argeo.server.jcr/src/org/argeo/jcr/tabular/JcrTabularRowIterator.java +++ /dev/null @@ -1,185 +0,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. - */ -package org.argeo.jcr.tabular; - -import java.io.InputStream; -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.ArrayBlockingQueue; - -import javax.jcr.Binary; -import javax.jcr.Node; -import javax.jcr.NodeIterator; -import javax.jcr.Property; -import javax.jcr.PropertyType; -import javax.jcr.RepositoryException; - -import org.apache.commons.io.IOUtils; -import org.argeo.jcr.ArgeoJcrException; -import org.argeo.jcr.ArgeoTypes; -import org.argeo.util.CsvParser; -import org.argeo.util.tabular.ArrayTabularRow; -import org.argeo.util.tabular.TabularColumn; -import org.argeo.util.tabular.TabularRow; -import org.argeo.util.tabular.TabularRowIterator; - -/** Iterates over the rows of a {@link ArgeoTypes#ARGEO_TABLE} node. */ -public class JcrTabularRowIterator implements TabularRowIterator { - private Boolean hasNext = null; - private Boolean parsingCompleted = false; - - private Long currentRowNumber = 0l; - - private List header = new ArrayList(); - - /** referenced so that we can close it */ - private Binary binary; - private InputStream in; - - private CsvParser csvParser; - private ArrayBlockingQueue> textLines; - - public JcrTabularRowIterator(Node tableNode) { - try { - for (NodeIterator it = tableNode.getNodes(); it.hasNext();) { - Node node = it.nextNode(); - if (node.isNodeType(ArgeoTypes.ARGEO_COLUMN)) { - Integer type = PropertyType.valueFromName(node.getProperty( - Property.JCR_REQUIRED_TYPE).getString()); - TabularColumn tc = new TabularColumn(node.getProperty( - Property.JCR_TITLE).getString(), type); - header.add(tc); - } - } - Node contentNode = tableNode.getNode(Property.JCR_CONTENT); - if (contentNode.isNodeType(ArgeoTypes.ARGEO_CSV)) { - textLines = new ArrayBlockingQueue>(1000); - csvParser = new CsvParser() { - protected void processLine(Integer lineNumber, - List header, List tokens) { - try { - textLines.put(tokens); - } catch (InterruptedException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } - // textLines.add(tokens); - if (hasNext == null) { - hasNext = true; - synchronized (JcrTabularRowIterator.this) { - JcrTabularRowIterator.this.notifyAll(); - } - } - } - }; - csvParser.setNoHeader(true); - binary = contentNode.getProperty(Property.JCR_DATA).getBinary(); - in = binary.getStream(); - Thread thread = new Thread(contentNode.getPath() + " reader") { - public void run() { - try { - csvParser.parse(in); - } finally { - parsingCompleted = true; - IOUtils.closeQuietly(in); - } - } - }; - thread.start(); - } - } catch (RepositoryException e) { - throw new ArgeoJcrException("Cannot read table " + tableNode, e); - } - } - - public synchronized boolean hasNext() { - // we don't know if there is anything available - // while (hasNext == null) - // try { - // wait(); - // } catch (InterruptedException e) { - // // silent - // // FIXME better deal with interruption - // Thread.currentThread().interrupt(); - // break; - // } - - // buffer not empty - if (!textLines.isEmpty()) - return true; - - // maybe the parsing is finished but the flag has not been set - while (!parsingCompleted && textLines.isEmpty()) - try { - wait(100); - } catch (InterruptedException e) { - // silent - // FIXME better deal with interruption - Thread.currentThread().interrupt(); - break; - } - - // buffer not empty - if (!textLines.isEmpty()) - return true; - - // (parsingCompleted && textLines.isEmpty()) - return false; - - // if (!hasNext && textLines.isEmpty()) { - // if (in != null) { - // IOUtils.closeQuietly(in); - // in = null; - // } - // if (binary != null) { - // JcrUtils.closeQuietly(binary); - // binary = null; - // } - // return false; - // } else - // return true; - } - - public synchronized TabularRow next() { - try { - List tokens = textLines.take(); - List objs = new ArrayList(tokens.size()); - for (String token : tokens) { - // TODO convert to other formats using header - objs.add(token); - } - currentRowNumber++; - return new ArrayTabularRow(objs); - } catch (InterruptedException e) { - // silent - // FIXME better deal with interruption - } - return null; - } - - public void remove() { - throw new UnsupportedOperationException(); - } - - public Long getCurrentRowNumber() { - return currentRowNumber; - } - - public List getHeader() { - return header; - } - -} diff --git a/org.argeo.server.jcr/src/org/argeo/jcr/tabular/JcrTabularWriter.java b/org.argeo.server.jcr/src/org/argeo/jcr/tabular/JcrTabularWriter.java deleted file mode 100644 index c3fd97cbd..000000000 --- a/org.argeo.server.jcr/src/org/argeo/jcr/tabular/JcrTabularWriter.java +++ /dev/null @@ -1,97 +0,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. - */ -package org.argeo.jcr.tabular; - -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.InputStream; -import java.util.List; - -import javax.jcr.Binary; -import javax.jcr.Node; -import javax.jcr.Property; -import javax.jcr.PropertyType; -import javax.jcr.RepositoryException; - -import org.apache.commons.io.IOUtils; -import org.argeo.jcr.ArgeoJcrException; -import org.argeo.jcr.ArgeoTypes; -import org.argeo.jcr.JcrUtils; -import org.argeo.util.CsvWriter; -import org.argeo.util.tabular.TabularColumn; -import org.argeo.util.tabular.TabularWriter; - -/** Write / reference tabular content in a JCR repository. */ -public class JcrTabularWriter implements TabularWriter { - private Node contentNode; - private ByteArrayOutputStream out; - private CsvWriter csvWriter; - - @SuppressWarnings("unused") - private final List columns; - - /** Creates a table node */ - public JcrTabularWriter(Node tableNode, List columns, - String contentNodeType) { - try { - this.columns = columns; - for (TabularColumn column : columns) { - String normalized = JcrUtils.replaceInvalidChars(column - .getName()); - Node columnNode = tableNode.addNode(normalized, - ArgeoTypes.ARGEO_COLUMN); - columnNode.setProperty(Property.JCR_TITLE, column.getName()); - if (column.getType() != null) - columnNode.setProperty(Property.JCR_REQUIRED_TYPE, - PropertyType.nameFromValue(column.getType())); - else - columnNode.setProperty(Property.JCR_REQUIRED_TYPE, - PropertyType.TYPENAME_STRING); - } - contentNode = tableNode.addNode(Property.JCR_CONTENT, - contentNodeType); - if (contentNodeType.equals(ArgeoTypes.ARGEO_CSV)) { - contentNode.setProperty(Property.JCR_MIMETYPE, "text/csv"); - contentNode.setProperty(Property.JCR_ENCODING, "UTF-8"); - out = new ByteArrayOutputStream(); - csvWriter = new CsvWriter(out); - } - } catch (RepositoryException e) { - throw new ArgeoJcrException("Cannot create table node " + tableNode, e); - } - } - - public void appendRow(Object[] row) { - csvWriter.writeLine(row); - } - - public void close() { - Binary binary = null; - InputStream in = null; - try { - // TODO parallelize with pipes and writing from another thread - in = new ByteArrayInputStream(out.toByteArray()); - binary = contentNode.getSession().getValueFactory() - .createBinary(in); - contentNode.setProperty(Property.JCR_DATA, binary); - } catch (RepositoryException e) { - throw new ArgeoJcrException("Cannot store data in " + contentNode, e); - } finally { - IOUtils.closeQuietly(in); - JcrUtils.closeQuietly(binary); - } - } -} diff --git a/org.argeo.server.jcr/src/org/argeo/jcr/unit/AbstractJcrTestCase.java b/org.argeo.server.jcr/src/org/argeo/jcr/unit/AbstractJcrTestCase.java deleted file mode 100644 index 1269a3ee5..000000000 --- a/org.argeo.server.jcr/src/org/argeo/jcr/unit/AbstractJcrTestCase.java +++ /dev/null @@ -1,133 +0,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. - */ -package org.argeo.jcr.unit; - -import java.io.File; -import java.security.AccessController; -import java.security.PrivilegedAction; - -import javax.jcr.Repository; -import javax.jcr.Session; -import javax.jcr.SimpleCredentials; -import javax.security.auth.Subject; -import javax.security.auth.login.LoginContext; -import javax.security.auth.login.LoginException; - -import org.apache.commons.io.FileUtils; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.argeo.jcr.ArgeoJcrException; - -import junit.framework.TestCase; - -public abstract class AbstractJcrTestCase extends TestCase { - private final static Log log = LogFactory.getLog(AbstractJcrTestCase.class); - - private Repository repository; - private Session session = null; - - public final static String LOGIN_CONTEXT_TEST_SYSTEM = "TEST_JACKRABBIT_ADMIN"; - - // protected abstract File getRepositoryFile() throws Exception; - - protected abstract Repository createRepository() throws Exception; - - protected abstract void clearRepository(Repository repository) - throws Exception; - - @Override - protected void setUp() throws Exception { - File homeDir = getHomeDir(); - FileUtils.deleteDirectory(homeDir); - repository = createRepository(); - } - - @Override - protected void tearDown() throws Exception { - if (session != null) { - session.logout(); - if (log.isTraceEnabled()) - log.trace("Logout session"); - } - clearRepository(repository); - } - - protected Session session() { - if (session != null && session.isLive()) - return session; - Session session; - if (getLoginContext() != null) { - LoginContext lc; - try { - lc = new LoginContext(getLoginContext()); - lc.login(); - } catch (LoginException e) { - throw new ArgeoJcrException("JAAS login failed", e); - } - session = Subject.doAs(lc.getSubject(), - new PrivilegedAction() { - - @Override - public Session run() { - return login(); - } - - }); - } else - session = login(); - this.session = session; - return this.session; - } - - protected String getLoginContext() { - return null; - } - - protected Session login() { - try { - if (log.isTraceEnabled()) - log.trace("Login session"); - Subject subject = Subject.getSubject(AccessController.getContext()); - if (subject != null) - return getRepository().login(); - else - return getRepository().login( - new SimpleCredentials("demo", "demo".toCharArray())); - } catch (Exception e) { - throw new ArgeoJcrException("Cannot login to repository", e); - } - } - - protected Repository getRepository() { - return repository; - } - - /** - * enables children class to set an existing repository in case it is not - * deleted on startup, to test migration by instance - */ - public void setRepository(Repository repository) { - this.repository = repository; - } - - protected File getHomeDir() { - File homeDir = new File(System.getProperty("java.io.tmpdir"), - AbstractJcrTestCase.class.getSimpleName() + "-" - + System.getProperty("user.name")); - return homeDir; - } - -} diff --git a/pom.xml b/pom.xml index 92be301de..3f8cc3384 100644 --- a/pom.xml +++ b/pom.xml @@ -1,5 +1,6 @@ - + 4.0.0 org.argeo.commons argeo-commons @@ -21,23 +22,23 @@ org.argeo.util + org.argeo.enterprise + org.argeo.jcr org.argeo.osgi.boot - org.argeo.server.jcr - - org.argeo.security.core org.argeo.eclipse.ui org.argeo.eclipse.ui.rap + org.argeo.eclipse.ui.workbench - org.argeo.cms.api + org.argeo.node.api org.argeo.cms - org.argeo.security.jackrabbit - - org.argeo.eclipse.ui.workbench - org.argeo.eclipse.ui.workbench.rap + org.argeo.security.ui - org.argeo.security.ui.admin - org.argeo.security.ui.rap + org.argeo.cms.ui.workbench + org.argeo.cms.ui.workbench.rap + + org.argeo.ext.jackrabbit + org.argeo.ext.rap.ui.workbench dep dist @@ -502,8 +503,10 @@ limitations under the License. - - + + @@ -528,8 +531,10 @@ limitations under the License. - - + +