Continue framework clean up.
authorMathieu Baudier <mbaudier@argeo.org>
Wed, 10 Aug 2016 14:35:37 +0000 (14:35 +0000)
committerMathieu Baudier <mbaudier@argeo.org>
Wed, 10 Aug 2016 14:35:37 +0000 (14:35 +0000)
git-svn-id: https://svn.argeo.org/commons/trunk@9078 4cfe0d0a-d680-48aa-b62c-e0a02a3f76cc

29 files changed:
org.argeo.cms.api/src/org/argeo/node/ArgeoLogListener.java [new file with mode: 0644]
org.argeo.cms.api/src/org/argeo/node/ArgeoLogger.java [new file with mode: 0644]
org.argeo.cms.api/src/org/argeo/node/RepoConf.java
org.argeo.cms/src/org/argeo/cms/internal/auth/ConsoleCallbackHandler.java
org.argeo.cms/src/org/argeo/cms/internal/auth/LocaleChoice.java [new file with mode: 0644]
org.argeo.cms/src/org/argeo/cms/internal/kernel/Activator.java
org.argeo.cms/src/org/argeo/cms/internal/kernel/CmsState.java
org.argeo.cms/src/org/argeo/cms/internal/kernel/KernelHeader.java [deleted file]
org.argeo.cms/src/org/argeo/cms/internal/kernel/KernelThread.java
org.argeo.cms/src/org/argeo/cms/internal/kernel/NodeLogger.java
org.argeo.cms/src/org/argeo/cms/internal/kernel/NodeUserAdmin.java
org.argeo.cms/src/org/argeo/cms/internal/kernel/RepositoryService.java
org.argeo.cms/src/org/argeo/cms/internal/kernel/SecurityProfile.java [new file with mode: 0644]
org.argeo.cms/src/org/argeo/cms/widgets/auth/CmsLogin.java
org.argeo.cms/src/org/argeo/cms/widgets/auth/CompositeCallbackHandler.java
org.argeo.security.core/ext/test/org/argeo/osgi/useradmin/LdifParserTest.java
org.argeo.security.core/src/org/argeo/osgi/useradmin/LdifParser.java [deleted file]
org.argeo.security.core/src/org/argeo/osgi/useradmin/LdifUserAdmin.java
org.argeo.security.core/src/org/argeo/osgi/useradmin/LdifWriter.java [deleted file]
org.argeo.security.core/src/org/argeo/util/naming/AttributesDictionary.java [new file with mode: 0644]
org.argeo.security.core/src/org/argeo/util/naming/LdifParser.java [new file with mode: 0644]
org.argeo.security.core/src/org/argeo/util/naming/LdifWriter.java [new file with mode: 0644]
org.argeo.security.ui/META-INF/spring/osgi.xml
org.argeo.security.ui/src/org/argeo/security/ui/views/AdminLogView.java
org.argeo.security.ui/src/org/argeo/security/ui/views/LogContentProvider.java
org.argeo.security.ui/src/org/argeo/security/ui/views/LogView.java
org.argeo.util/src/org/argeo/ArgeoLogListener.java [deleted file]
org.argeo.util/src/org/argeo/ArgeoLogger.java [deleted file]
org.argeo.util/src/org/argeo/util/LocaleChoice.java [deleted 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
new file mode 100644 (file)
index 0000000..698dfe1
--- /dev/null
@@ -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.cms.api/src/org/argeo/node/ArgeoLogger.java b/org.argeo.cms.api/src/org/argeo/node/ArgeoLogger.java
new file mode 100644 (file)
index 0000000..213286d
--- /dev/null
@@ -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);
+}
index 90d33322fa6b8fb2befd2447db3851ad61500f95..be4f6f7f79716c66084f75eb986d0013d1f5fffe 100644 (file)
@@ -5,6 +5,7 @@ public enum RepoConf implements EnumAD {
        /** Repository type */
        type("localfs"),
        /** Default workspace */
+       @Deprecated
        defaultWorkspace("main"),
        /** Database URL */
        dburl(null),
@@ -15,14 +16,13 @@ public enum RepoConf implements EnumAD {
 
        /** The identifier (can be an URL locating the repo) */
        labeledUri(null),
-
-       httpPort(8080),
        //
        // JACKRABBIT SPECIFIC
        //
        /** Maximum database pool size */
        maxPoolSize(10),
        /** Maximum cache size in MB */
+       @Deprecated
        maxCacheMB(null),
        /** Bundle cache size in MB */
        bundleCacheMB(8),
index acb75d1e52b0d57655d85b785a4cca9d8a2f14c4..4edcb6341f27d35a9a319e49af3a6ede27420a6f 100644 (file)
@@ -14,7 +14,6 @@ import javax.security.auth.callback.TextOutputCallback;
 import javax.security.auth.callback.UnsupportedCallbackException;
 
 import org.argeo.ArgeoException;
-import org.argeo.util.LocaleChoice;
 
 /** Callback handler to be used with a command line UI. */
 public class ConsoleCallbackHandler implements CallbackHandler {
diff --git a/org.argeo.cms/src/org/argeo/cms/internal/auth/LocaleChoice.java b/org.argeo.cms/src/org/argeo/cms/internal/auth/LocaleChoice.java
new file mode 100644 (file)
index 0000000..9ec9f7a
--- /dev/null
@@ -0,0 +1,132 @@
+/*
+ * Copyright (C) 2007-2012 Argeo GmbH
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *         http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.argeo.cms.internal.auth;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Locale;
+
+import javax.security.auth.callback.LanguageCallback;
+
+import org.argeo.ArgeoException;
+
+/** Choose in a list of locales. TODO: replace with {@link LanguageCallback} */
+public class LocaleChoice {
+       private final List<Locale> locales;
+
+       private Integer selectedIndex = null;
+       private final Integer defaultIndex;
+
+       public LocaleChoice(List<Locale> locales, Locale defaultLocale) {
+               Integer defaultIndex = null;
+               this.locales = Collections.unmodifiableList(locales);
+               for (int i = 0; i < locales.size(); i++)
+                       if (locales.get(i).equals(defaultLocale))
+                               defaultIndex = i;
+
+               // based on language only
+               if (defaultIndex == null)
+                       for (int i = 0; i < locales.size(); i++)
+                               if (locales.get(i).getLanguage().equals(defaultLocale.getLanguage()))
+                                       defaultIndex = i;
+
+               if (defaultIndex == null)
+                       throw new ArgeoException("Default locale " + defaultLocale + " is not in available locales " + locales);
+               this.defaultIndex = defaultIndex;
+
+               this.selectedIndex = defaultIndex;
+       }
+
+       /**
+        * Convenience constructor based on a comma separated list of iso codes (en,
+        * en_US, fr_CA, etc.). Default selection is default locale.
+        */
+       public LocaleChoice(String locales, Locale defaultLocale) {
+               this(asLocaleList(locales), defaultLocale);
+       }
+
+       public String[] getSupportedLocalesLabels() {
+               String[] labels = new String[locales.size()];
+               for (int i = 0; i < locales.size(); i++) {
+                       Locale locale = locales.get(i);
+                       if (locale.getCountry().equals(""))
+                               labels[i] = locale.getDisplayLanguage(locale) + " [" + locale.getLanguage() + "]";
+                       else
+                               labels[i] = locale.getDisplayLanguage(locale) + " (" + locale.getDisplayCountry(locale) + ") ["
+                                               + locale.getLanguage() + "_" + locale.getCountry() + "]";
+
+               }
+               return labels;
+       }
+
+       public Locale getSelectedLocale() {
+               if (selectedIndex == null)
+                       return null;
+               return locales.get(selectedIndex);
+       }
+
+       public void setSelectedIndex(Integer selectedIndex) {
+               this.selectedIndex = selectedIndex;
+       }
+
+       public Integer getSelectedIndex() {
+               return selectedIndex;
+       }
+
+       public Integer getDefaultIndex() {
+               return defaultIndex;
+       }
+
+       public List<Locale> getLocales() {
+               return locales;
+       }
+
+       public Locale getDefaultLocale() {
+               return locales.get(getDefaultIndex());
+       }
+
+       /** Returns null if argument is null. */
+       public static List<Locale> asLocaleList(Object locales) {
+               if (locales == null)
+                       return null;
+               ArrayList<Locale> availableLocales = new ArrayList<Locale>();
+               String[] codes = locales.toString().split(",");
+               for (int i = 0; i < codes.length; i++) {
+                       String code = codes[i];
+                       // variant not supported
+                       int indexUnd = code.indexOf("_");
+                       Locale locale;
+                       if (indexUnd > 0) {
+                               String language = code.substring(0, indexUnd);
+                               String country = code.substring(indexUnd + 1);
+                               locale = new Locale(language, country);
+                       } else {
+                               locale = new Locale(code);
+                       }
+                       availableLocales.add(locale);
+               }
+               return availableLocales;
+       }
+
+       public static void main(String[] args) {
+               for (String isoL : Locale.getISOLanguages()) {
+                       Locale locale = new Locale(isoL);
+                       System.out.println(isoL + "\t" + locale.getDisplayLanguage() + "\t" + locale.getDisplayLanguage(locale));
+               }
+       }
+
+}
index 3546647dda2f79355dec99deb42095c4caa60dd9..03cbbd90df2473c0bbc74848364a2c9d7fb1bec9 100644 (file)
@@ -9,8 +9,8 @@ import java.util.Locale;
 
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
-import org.argeo.ArgeoLogger;
 import org.argeo.cms.CmsException;
+import org.argeo.node.ArgeoLogger;
 import org.argeo.node.NodeConstants;
 import org.argeo.node.NodeState;
 import org.argeo.node.RepoConf;
@@ -93,7 +93,7 @@ public class Activator implements BundleActivator {
                                log.debug("Clean node state");
                        Dictionary<String, Object> envProps = getStatePropertiesFromEnvironment();
                        // Use the UUID of the first framework run as state UUID
-                       cn = KernelUtils.getFrameworkProp(Constants.FRAMEWORK_UUID);
+                       cn = bc.getProperty(Constants.FRAMEWORK_UUID);
                        envProps.put(NodeConstants.CN, cn);
                        nodeConf.update(envProps);
                } else {
index 7ad61e50f5244720f7ac34c00d898574ab5722f4..64a9d170bed6b551a49ea6c9a3019a65f6ca93a9 100644 (file)
@@ -3,9 +3,8 @@ package org.argeo.cms.internal.kernel;
 import static bitronix.tm.TransactionManagerServices.getTransactionManager;
 import static bitronix.tm.TransactionManagerServices.getTransactionSynchronizationRegistry;
 import static java.util.Locale.ENGLISH;
+import static org.argeo.cms.internal.auth.LocaleChoice.asLocaleList;
 import static org.argeo.cms.internal.kernel.KernelUtils.getFrameworkProp;
-import static org.argeo.util.LocaleChoice.asLocaleList;
-import static org.osgi.framework.Constants.FRAMEWORK_UUID;
 
 import java.io.File;
 import java.io.IOException;
@@ -15,10 +14,12 @@ import java.net.UnknownHostException;
 import java.nio.file.DirectoryStream;
 import java.nio.file.Files;
 import java.nio.file.Path;
+import java.util.ArrayList;
 import java.util.Dictionary;
 import java.util.Hashtable;
 import java.util.List;
 import java.util.Locale;
+import java.util.UUID;
 
 import javax.jcr.RepositoryFactory;
 import javax.transaction.TransactionManager;
@@ -73,15 +74,13 @@ public class CmsState implements NodeState, ManagedService {
        private Locale defaultLocale;
        private List<Locale> locales = null;
 
-       // Standalone services
-       private BitronixTransactionManager transactionManager;
-       private BitronixTransactionSynchronizationRegistry transactionSynchronizationRegistry;
-       private NodeRepositoryFactory repositoryFactory;
-
-       // Security
-       private NodeUserAdmin userAdmin;
-       private RepositoryServiceFactory repositoryServiceFactory;
-       private RepositoryService repositoryService;
+       // private BitronixTransactionManager transactionManager;
+       // private BitronixTransactionSynchronizationRegistry
+       // transactionSynchronizationRegistry;
+       // private NodeRepositoryFactory repositoryFactory;
+       // private NodeUserAdmin userAdmin;
+       // private RepositoryServiceFactory repositoryServiceFactory;
+       // private RepositoryService repositoryService;
 
        // Deployment
        private final CmsDeployment nodeDeployment = new CmsDeployment();
@@ -89,8 +88,9 @@ public class CmsState implements NodeState, ManagedService {
        private boolean cleanState = false;
        private URI nodeRepoUri = null;
 
-       ThreadGroup threadGroup = new ThreadGroup("CMS State");
+       private ThreadGroup threadGroup = new ThreadGroup("CMS");
        private KernelThread kernelThread;
+       private List<Runnable> shutdownHooks = new ArrayList<>();
 
        private String hostname;
 
@@ -123,23 +123,14 @@ public class CmsState implements NodeState, ManagedService {
 
                        nodeRepoUri = KernelUtils.getOsgiInstanceUri("repos/node");
 
-                       // pre-requisite
                        initI18n(properties);
-                       initTrackers();
-                       // standalone services
-                       initTransactionManager();
-                       initRepositoryFactory();
-                       // UI
-                       initUi();
-                       // Deployment
+                       initServices();
                        initDeployConfigs(properties);
-                       initUserAdmin();
-                       initRepositories(properties);
                        initWebServer();
                        initNodeDeployment();
 
                        // kernel thread
-                       kernelThread = new KernelThread(this);
+                       kernelThread = new KernelThread(threadGroup, "Kernel Thread");
                        kernelThread.setContextClassLoader(getClass().getClassLoader());
                        kernelThread.start();
                } catch (Exception e) {
@@ -147,11 +138,6 @@ public class CmsState implements NodeState, ManagedService {
                }
        }
 
-       private void initTrackers() {
-               new ServiceTracker<HttpService, HttpService>(bc, HttpService.class, new PrepareHttpStc()).open();
-               new ServiceTracker<>(bc, RepositoryContext.class, new RepositoryContextStc()).open();
-       }
-
        private void initI18n(Dictionary<String, ?> stateProps) {
                Object defaultLocaleValue = stateProps.get(NodeConstants.I18N_DEFAULT_LOCALE);
                defaultLocale = defaultLocaleValue != null ? new Locale(defaultLocaleValue.toString())
@@ -159,9 +145,57 @@ public class CmsState implements NodeState, ManagedService {
                locales = asLocaleList(stateProps.get(NodeConstants.I18N_LOCALES));
        }
 
+       private void initServices() {
+               // trackers
+               new ServiceTracker<HttpService, HttpService>(bc, HttpService.class, new PrepareHttpStc()).open();
+               new ServiceTracker<>(bc, RepositoryContext.class, new RepositoryContextStc()).open();
+
+               initTransactionManager();
+
+               // JCR
+               RepositoryServiceFactory repositoryServiceFactory = new RepositoryServiceFactory();
+               shutdownHooks.add(() -> repositoryServiceFactory.shutdown());
+               bc.registerService(ManagedServiceFactory.class, repositoryServiceFactory,
+                               LangUtils.init(Constants.SERVICE_PID, NodeConstants.JACKRABBIT_FACTORY_PID));
+
+               NodeRepositoryFactory repositoryFactory = new NodeRepositoryFactory();
+               bc.registerService(RepositoryFactory.class, repositoryFactory, null);
+
+               RepositoryService repositoryService = new RepositoryService();
+               shutdownHooks.add(() -> repositoryService.shutdown());
+               bc.registerService(LangUtils.names(ManagedService.class, MetaTypeProvider.class), repositoryService,
+                               LangUtils.init(Constants.SERVICE_PID, NodeConstants.NODE_REPO_PID));
+
+               // Security
+               NodeUserAdmin userAdmin = new NodeUserAdmin();
+               shutdownHooks.add(() -> userAdmin.destroy());
+               Dictionary<String, Object> props = userAdmin.currentState();
+               props.put(Constants.SERVICE_PID, NodeConstants.NODE_USER_ADMIN_PID);
+               bc.registerService(UserAdmin.class, userAdmin, props);
+
+               // UI
+               bc.registerService(ApplicationConfiguration.class, new MaintenanceUi(),
+                               LangUtils.init(PROPERTY_CONTEXT_NAME, "system"));
+               bc.registerService(ApplicationConfiguration.class, new UserUi(), LangUtils.init(PROPERTY_CONTEXT_NAME, "user"));
+       }
+       // private void initUserAdmin() {
+       // userAdmin = new NodeUserAdmin();
+       // // register
+       // Dictionary<String, Object> props = userAdmin.currentState();
+       // props.put(Constants.SERVICE_PID, NodeConstants.NODE_USER_ADMIN_PID);
+       // // TODO use ManagedService
+       // bc.registerService(UserAdmin.class, userAdmin, props);
+       // }
+
        private void initTransactionManager() {
+               // TODO manage it in a managed service, as startup could be long
+               ServiceReference<TransactionManager> existingTm = bc.getServiceReference(TransactionManager.class);
+               if (existingTm != null) {
+                       if (log.isDebugEnabled())
+                               log.debug("Using provided transaction manager " + existingTm);
+               }
                bitronix.tm.Configuration tmConf = TransactionManagerServices.getConfiguration();
-               tmConf.setServerId(getFrameworkProp(FRAMEWORK_UUID));
+               tmConf.setServerId(UUID.randomUUID().toString());
 
                Bundle bitronixBundle = FrameworkUtil.getBundle(bitronix.tm.Configuration.class);
                File tmBaseDir = bitronixBundle.getDataFile(KernelConstants.DIR_TRANSACTIONS);
@@ -171,26 +205,31 @@ public class CmsState implements NodeState, ManagedService {
                File tmDir2 = new File(tmBaseDir, "btm2");
                tmDir2.mkdirs();
                tmConf.setLogPart2Filename(new File(tmDir2, tmDir2.getName() + ".tlog").getAbsolutePath());
-               transactionManager = getTransactionManager();
-               transactionSynchronizationRegistry = getTransactionSynchronizationRegistry();
+
+               BitronixTransactionManager transactionManager = getTransactionManager();
+               shutdownHooks.add(() -> transactionManager.shutdown());
+               BitronixTransactionSynchronizationRegistry transactionSynchronizationRegistry = getTransactionSynchronizationRegistry();
                // register
                bc.registerService(TransactionManager.class, transactionManager, null);
                bc.registerService(UserTransaction.class, transactionManager, null);
                bc.registerService(TransactionSynchronizationRegistry.class, transactionSynchronizationRegistry, null);
+               if (log.isDebugEnabled())
+                       log.debug("Initialised default Bitronix transaction manager");
        }
 
-       private void initRepositoryFactory() {
-               // TODO rationalise RepositoryFactory
-               repositoryFactory = new NodeRepositoryFactory();
-               // register
-               bc.registerService(RepositoryFactory.class, repositoryFactory, null);
-       }
+       // private void initRepositoryFactory() {
+       // // TODO rationalise RepositoryFactory
+       // repositoryFactory = new NodeRepositoryFactory();
+       // // register
+       // bc.registerService(RepositoryFactory.class, repositoryFactory, null);
+       // }
 
-       private void initUi() {
-               bc.registerService(ApplicationConfiguration.class, new MaintenanceUi(),
-                               LangUtils.init(PROPERTY_CONTEXT_NAME, "system"));
-               bc.registerService(ApplicationConfiguration.class, new UserUi(), LangUtils.init(PROPERTY_CONTEXT_NAME, "user"));
-       }
+       // private void initUi() {
+       // bc.registerService(ApplicationConfiguration.class, new MaintenanceUi(),
+       // LangUtils.init(PROPERTY_CONTEXT_NAME, "system"));
+       // bc.registerService(ApplicationConfiguration.class, new UserUi(),
+       // LangUtils.init(PROPERTY_CONTEXT_NAME, "user"));
+       // }
 
        private void initDeployConfigs(Dictionary<String, ?> stateProps) throws IOException {
                Path deployPath = KernelUtils.getOsgiInstancePath(KernelConstants.DIR_NODE + '/' + KernelConstants.DIR_DEPLOY);
@@ -228,25 +267,20 @@ public class CmsState implements NodeState, ManagedService {
                }
        }
 
-       private void initUserAdmin() {
-               userAdmin = new NodeUserAdmin();
-               // register
-               Dictionary<String, Object> props = userAdmin.currentState();
-               props.put(Constants.SERVICE_PID, NodeConstants.NODE_USER_ADMIN_PID);
-               // TODO use ManagedService
-               bc.registerService(UserAdmin.class, userAdmin, props);
-       }
-
-       private void initRepositories(Dictionary<String, ?> stateProps) throws IOException {
-               // register
-               repositoryServiceFactory = new RepositoryServiceFactory();
-               bc.registerService(ManagedServiceFactory.class, repositoryServiceFactory,
-                               LangUtils.init(Constants.SERVICE_PID, NodeConstants.JACKRABBIT_FACTORY_PID));
-
-               repositoryService = new RepositoryService();
-               Dictionary<String, Object> regProps = LangUtils.init(Constants.SERVICE_PID, NodeConstants.NODE_REPO_PID);
-               bc.registerService(LangUtils.names(ManagedService.class, MetaTypeProvider.class), repositoryService, regProps);
-       }
+       // private void initRepositories(Dictionary<String, ?> stateProps) throws
+       // IOException {
+       // // register
+       // repositoryServiceFactory = new RepositoryServiceFactory();
+       // bc.registerService(ManagedServiceFactory.class, repositoryServiceFactory,
+       // LangUtils.init(Constants.SERVICE_PID,
+       // NodeConstants.JACKRABBIT_FACTORY_PID));
+       //
+       // repositoryService = new RepositoryService();
+       // Dictionary<String, Object> regProps =
+       // LangUtils.init(Constants.SERVICE_PID, NodeConstants.NODE_REPO_PID);
+       // bc.registerService(LangUtils.names(ManagedService.class,
+       // MetaTypeProvider.class), repositoryService, regProps);
+       // }
 
        private void initWebServer() {
                String httpPort = getFrameworkProp("org.osgi.service.http.port");
@@ -291,21 +325,35 @@ public class CmsState implements NodeState, ManagedService {
        }
 
        void shutdown() {
+               // if (transactionManager != null)
+               // transactionManager.shutdown();
+               // if (userAdmin != null)
+               // userAdmin.destroy();
+               // if (repositoryServiceFactory != null)
+               // repositoryServiceFactory.shutdown();
+
+               applyShutdownHooks();
+
                if (kernelThread != null)
                        kernelThread.destroyAndJoin();
 
-               if (transactionManager != null)
-                       transactionManager.shutdown();
-               if (userAdmin != null)
-                       userAdmin.destroy();
-               if (repositoryServiceFactory != null)
-                       repositoryServiceFactory.shutdown();
+               if (log.isDebugEnabled())
+                       log.debug("## CMS STOPPED");
+       }
 
+       /** Apply shutdown hoos in reverse order. */
+       private void applyShutdownHooks() {
+               for (int i = shutdownHooks.size() - 1; i >= 0; i--) {
+                       try {
+                               // new Thread(shutdownHooks.get(i), "CMS Shutdown Hook #" +
+                               // i).start();
+                               shutdownHooks.get(i).run();
+                       } catch (Exception e) {
+                               log.error("Could not run shutdown hook #" + i);
+                       }
+               }
                // Clean hanging Gogo shell thread
                new GogoShellKiller().start();
-
-               if (log.isDebugEnabled())
-                       log.debug("## CMS STOPPED");
        }
 
        private Dictionary<String, Object> getNodeConfig(Dictionary<String, ?> properties) {
@@ -415,7 +463,7 @@ public class CmsState implements NodeState, ManagedService {
        private class GogoShellKiller extends Thread {
 
                public GogoShellKiller() {
-                       super("Gogo shell killer");
+                       super("Gogo Shell Killer");
                        setDaemon(true);
                }
 
diff --git a/org.argeo.cms/src/org/argeo/cms/internal/kernel/KernelHeader.java b/org.argeo.cms/src/org/argeo/cms/internal/kernel/KernelHeader.java
deleted file mode 100644 (file)
index f82f6b1..0000000
+++ /dev/null
@@ -1,10 +0,0 @@
-package org.argeo.cms.internal.kernel;
-
-import java.util.List;
-import java.util.Locale;
-
-public interface KernelHeader {
-       public Locale getDefaultLocale();
-
-       public List<Locale> getLocales();
-}
index bd5828602e808ac07432c360b90588aece0460b0..944a7cd3dbcf3bd42b59afdc37ce535c49bff558 100644 (file)
@@ -30,8 +30,8 @@ class KernelThread extends Thread {
        @SuppressWarnings("unused")
        private long cycle = 0l;
 
-       public KernelThread(CmsState cmState) {
-               super(cmState.threadGroup, cmState.getClass().getSimpleName());
+       public KernelThread(ThreadGroup threadGroup, String name) {
+               super(threadGroup, name);
        }
 
        private void doSmallestPeriod() {
index de28ac11897057a430cff0e4e4f47292f5b5aaeb..b9475446e5c50a3a04f25030f73ee84e8c9f0bc6 100644 (file)
@@ -38,9 +38,9 @@ import org.apache.log4j.Logger;
 import org.apache.log4j.PropertyConfigurator;
 import org.apache.log4j.spi.LoggingEvent;
 import org.argeo.ArgeoException;
-import org.argeo.ArgeoLogListener;
-import org.argeo.ArgeoLogger;
 import org.argeo.cms.auth.CurrentUser;
+import org.argeo.node.ArgeoLogListener;
+import org.argeo.node.ArgeoLogger;
 import org.osgi.service.log.LogEntry;
 import org.osgi.service.log.LogListener;
 import org.osgi.service.log.LogReaderService;
index 3cc3dbfb39c4e95684e74fb64a78a77717cdd514..8f1c36860e525e73b2d1c2a0c1d0a6dde20193e0 100644 (file)
@@ -36,6 +36,8 @@ import org.osgi.framework.BundleContext;
 import org.osgi.framework.FrameworkUtil;
 import org.osgi.framework.InvalidSyntaxException;
 import org.osgi.framework.ServiceReference;
+import org.osgi.service.cm.ConfigurationException;
+import org.osgi.service.cm.ManagedService;
 import org.osgi.service.useradmin.Authorization;
 import org.osgi.service.useradmin.Role;
 import org.osgi.service.useradmin.User;
@@ -49,7 +51,7 @@ import bitronix.tm.resource.ehcache.EhCacheXAResourceProducer;
  * Aggregates multiple {@link UserDirectory} and integrates them with this node
  * system roles.
  */
-public class NodeUserAdmin implements UserAdmin, KernelConstants {
+class NodeUserAdmin implements UserAdmin, ManagedService, KernelConstants {
        private final static Log log = LogFactory.getLog(NodeUserAdmin.class);
        final static LdapName ROLES_BASE;
        static {
@@ -86,6 +88,10 @@ public class NodeUserAdmin implements UserAdmin, KernelConstants {
                new ServiceTracker<>(bc, TransactionManager.class, new TransactionManagerStc()).open();
        }
 
+       @Override
+       public void updated(Dictionary<String, ?> properties) throws ConfigurationException {
+       }
+
        private class TransactionManagerStc implements ServiceTrackerCustomizer<TransactionManager, TransactionManager> {
 
                @Override
index ed8be17eafc69c333cebd76325d5c132a02d4a01..8525174e23e3e0492ce9462c6cd600c744b4c48e 100644 (file)
@@ -23,7 +23,7 @@ public class RepositoryService implements ManagedService, MetaTypeProvider {
        private ServiceRegistration<RepositoryContext> repositoryContextReg;
 
        @Override
-       public synchronized void updated(Dictionary<String, ?> properties) throws ConfigurationException {
+       public void updated(Dictionary<String, ?> properties) throws ConfigurationException {
                if (properties == null)
                        return;
 
@@ -52,7 +52,7 @@ public class RepositoryService implements ManagedService, MetaTypeProvider {
 
        }
 
-       public synchronized void shutdown() {
+       public void shutdown() {
                if (repositoryContextReg == null)
                        return;
                RepositoryContext repositoryContext = bc.getService(repositoryContextReg.getReference());
diff --git a/org.argeo.cms/src/org/argeo/cms/internal/kernel/SecurityProfile.java b/org.argeo.cms/src/org/argeo/cms/internal/kernel/SecurityProfile.java
new file mode 100644 (file)
index 0000000..358b212
--- /dev/null
@@ -0,0 +1,288 @@
+package org.argeo.cms.internal.kernel;
+
+import java.io.FilePermission;
+import java.lang.reflect.ReflectPermission;
+import java.net.SocketPermission;
+import java.security.AllPermission;
+import java.util.PropertyPermission;
+
+import javax.management.MBeanPermission;
+import javax.management.MBeanServerPermission;
+import javax.management.MBeanTrustPermission;
+import javax.security.auth.AuthPermission;
+
+import org.osgi.framework.AdminPermission;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.FrameworkUtil;
+import org.osgi.framework.ServicePermission;
+import org.osgi.service.cm.ConfigurationPermission;
+import org.osgi.service.condpermadmin.BundleLocationCondition;
+import org.osgi.service.condpermadmin.ConditionInfo;
+import org.osgi.service.condpermadmin.ConditionalPermissionAdmin;
+import org.osgi.service.condpermadmin.ConditionalPermissionInfo;
+import org.osgi.service.condpermadmin.ConditionalPermissionUpdate;
+import org.osgi.service.permissionadmin.PermissionInfo;
+
+import bitronix.tm.BitronixTransactionManager;
+
+public interface SecurityProfile {
+       BundleContext bc = FrameworkUtil.getBundle(SecurityProfile.class).getBundleContext();
+
+       default void applySystemPermissions(ConditionalPermissionAdmin permissionAdmin) {
+               ConditionalPermissionUpdate update = permissionAdmin.newConditionalPermissionUpdate();
+               // Self
+               update.getConditionalPermissionInfos()
+                               .add(permissionAdmin.newConditionalPermissionInfo(null,
+                                               new ConditionInfo[] { new ConditionInfo(BundleLocationCondition.class.getName(),
+                                                               new String[] { locate(SecurityProfile.class) }) },
+                                               new PermissionInfo[] { new PermissionInfo(AllPermission.class.getName(), null, null) },
+                                               ConditionalPermissionInfo.ALLOW));
+               update.getConditionalPermissionInfos()
+                               .add(permissionAdmin.newConditionalPermissionInfo(null,
+                                               new ConditionInfo[] { new ConditionInfo(BundleLocationCondition.class.getName(),
+                                                               new String[] { bc.getBundle(0).getLocation() }) },
+                                               new PermissionInfo[] { new PermissionInfo(AllPermission.class.getName(), null, null) },
+                                               ConditionalPermissionInfo.ALLOW));
+               // All
+               // FIXME understand why Jetty and Jackrabbit require that
+               update.getConditionalPermissionInfos()
+                               .add(permissionAdmin.newConditionalPermissionInfo(null, null, new PermissionInfo[] {
+                                               new PermissionInfo(SocketPermission.class.getName(), "localhost:7070", "listen,resolve"),
+                                               new PermissionInfo(FilePermission.class.getName(), "<<ALL FILES>>", "read,write,delete"),
+                                               new PermissionInfo(PropertyPermission.class.getName(), "DEBUG", "read"),
+                                               new PermissionInfo(PropertyPermission.class.getName(), "STOP.*", "read"),
+                                               new PermissionInfo(PropertyPermission.class.getName(), "org.apache.jackrabbit.*", "read"),
+                                               new PermissionInfo(RuntimePermission.class.getName(), "*", "*"), },
+                                               ConditionalPermissionInfo.ALLOW));
+
+               // Eclipse
+               // update.getConditionalPermissionInfos()
+               // .add(permissionAdmin.newConditionalPermissionInfo(null,
+               // new ConditionInfo[] { new
+               // ConditionInfo(BundleLocationCondition.class.getName(),
+               // new String[] { "*/org.eclipse.*" }) },
+               // new PermissionInfo[] { new
+               // PermissionInfo(RuntimePermission.class.getName(), "*", "*"),
+               // new PermissionInfo(AdminPermission.class.getName(), "*", "*"),
+               // new PermissionInfo(ServicePermission.class.getName(), "*", "get"),
+               // new PermissionInfo(ServicePermission.class.getName(), "*",
+               // "register"),
+               // new PermissionInfo(TopicPermission.class.getName(), "*", "publish"),
+               // new PermissionInfo(TopicPermission.class.getName(), "*",
+               // "subscribe"),
+               // new PermissionInfo(PropertyPermission.class.getName(), "osgi.*",
+               // "read"),
+               // new PermissionInfo(PropertyPermission.class.getName(), "eclipse.*",
+               // "read"),
+               // new PermissionInfo(PropertyPermission.class.getName(),
+               // "org.eclipse.*", "read"),
+               // new PermissionInfo(PropertyPermission.class.getName(), "equinox.*",
+               // "read"),
+               // new PermissionInfo(PropertyPermission.class.getName(), "xml.*",
+               // "read"),
+               // new PermissionInfo("org.eclipse.equinox.log.LogPermission", "*",
+               // "log"), },
+               // ConditionalPermissionInfo.ALLOW));
+               update.getConditionalPermissionInfos()
+                               .add(permissionAdmin.newConditionalPermissionInfo(null,
+                                               new ConditionInfo[] { new ConditionInfo(BundleLocationCondition.class.getName(),
+                                                               new String[] { "*/org.eclipse.*" }) },
+                                               new PermissionInfo[] { new PermissionInfo(AllPermission.class.getName(), null, null), },
+                                               ConditionalPermissionInfo.ALLOW));
+               update.getConditionalPermissionInfos()
+                               .add(permissionAdmin.newConditionalPermissionInfo(null,
+                                               new ConditionInfo[] { new ConditionInfo(BundleLocationCondition.class.getName(),
+                                                               new String[] { "*/org.apache.felix.*" }) },
+                                               new PermissionInfo[] { new PermissionInfo(AllPermission.class.getName(), null, null), },
+                                               ConditionalPermissionInfo.ALLOW));
+
+               // Configuration admin
+//             update.getConditionalPermissionInfos().add(permissionAdmin.newConditionalPermissionInfo(null,
+//                             new ConditionInfo[] { new ConditionInfo(BundleLocationCondition.class.getName(),
+//                                             new String[] { locate(configurationAdmin.getService().getClass()) }) },
+//                             new PermissionInfo[] { new PermissionInfo(ConfigurationPermission.class.getName(), "*", "configure"),
+//                                             new PermissionInfo(AdminPermission.class.getName(), "*", "*"),
+//                                             new PermissionInfo(PropertyPermission.class.getName(), "osgi.*", "read"), },
+//                             ConditionalPermissionInfo.ALLOW));
+
+               // Bitronix
+               update.getConditionalPermissionInfos().add(permissionAdmin.newConditionalPermissionInfo(null,
+                               new ConditionInfo[] { new ConditionInfo(BundleLocationCondition.class.getName(),
+                                               new String[] { locate(BitronixTransactionManager.class) }) },
+                               new PermissionInfo[] { new PermissionInfo(PropertyPermission.class.getName(), "bitronix.tm.*", "read"),
+                                               new PermissionInfo(RuntimePermission.class.getName(), "getClassLoader", null),
+                                               new PermissionInfo(MBeanServerPermission.class.getName(), "createMBeanServer", null),
+                                               new PermissionInfo(MBeanPermission.class.getName(), "bitronix.tm.*", "registerMBean"),
+                                               new PermissionInfo(MBeanTrustPermission.class.getName(), "register", null) },
+                               ConditionalPermissionInfo.ALLOW));
+
+               // DS
+               Bundle dsBundle = findBundle("org.eclipse.equinox.ds");
+               update.getConditionalPermissionInfos().add(permissionAdmin.newConditionalPermissionInfo(null,
+                               new ConditionInfo[] { new ConditionInfo(BundleLocationCondition.class.getName(),
+                                               new String[] { dsBundle.getLocation() }) },
+                               new PermissionInfo[] { new PermissionInfo(ConfigurationPermission.class.getName(), "*", "configure"),
+                                               new PermissionInfo(AdminPermission.class.getName(), "*", "*"),
+                                               new PermissionInfo(ServicePermission.class.getName(), "*", "get"),
+                                               new PermissionInfo(ServicePermission.class.getName(), "*", "register"),
+                                               new PermissionInfo(PropertyPermission.class.getName(), "osgi.*", "read"),
+                                               new PermissionInfo(PropertyPermission.class.getName(), "xml.*", "read"),
+                                               new PermissionInfo(PropertyPermission.class.getName(), "equinox.*", "read"),
+                                               new PermissionInfo(RuntimePermission.class.getName(), "accessDeclaredMembers", null),
+                                               new PermissionInfo(RuntimePermission.class.getName(), "getClassLoader", null),
+                                               new PermissionInfo(ReflectPermission.class.getName(), "suppressAccessChecks", null), },
+                               ConditionalPermissionInfo.ALLOW));
+
+               // Jetty
+               Bundle jettyUtilBundle = findBundle("org.eclipse.equinox.http.jetty");
+               update.getConditionalPermissionInfos().add(permissionAdmin.newConditionalPermissionInfo(null,
+                               new ConditionInfo[] { new ConditionInfo(BundleLocationCondition.class.getName(),
+                                               new String[] { "*/org.eclipse.jetty.*" }) },
+                               new PermissionInfo[] {
+                                               new PermissionInfo(FilePermission.class.getName(), "<<ALL FILES>>", "read,write,delete"), },
+                               ConditionalPermissionInfo.ALLOW));
+
+               // Blueprint
+               Bundle blueprintBundle = findBundle("org.eclipse.gemini.blueprint.core");
+               update.getConditionalPermissionInfos()
+                               .add(permissionAdmin.newConditionalPermissionInfo(null,
+                                               new ConditionInfo[] { new ConditionInfo(BundleLocationCondition.class.getName(),
+                                                               new String[] { blueprintBundle.getLocation() }) },
+                                               new PermissionInfo[] { new PermissionInfo(RuntimePermission.class.getName(), "*", null),
+                                                               new PermissionInfo(AdminPermission.class.getName(), "*", "*"), },
+                                               ConditionalPermissionInfo.ALLOW));
+               Bundle blueprintExtenderBundle = findBundle("org.eclipse.gemini.blueprint.extender");
+               update.getConditionalPermissionInfos()
+                               .add(permissionAdmin
+                                               .newConditionalPermissionInfo(null,
+                                                               new ConditionInfo[] { new ConditionInfo(BundleLocationCondition.class.getName(),
+                                                                               new String[] { blueprintExtenderBundle.getLocation() }) },
+                                                               new PermissionInfo[] { new PermissionInfo(RuntimePermission.class.getName(), "*", null),
+                                                                               new PermissionInfo(PropertyPermission.class.getName(), "org.eclipse.gemini.*",
+                                                                                               "read"),
+                                                                               new PermissionInfo(AdminPermission.class.getName(), "*", "*"),
+                                                                               new PermissionInfo(ServicePermission.class.getName(), "*", "register"), },
+                                                               ConditionalPermissionInfo.ALLOW));
+               Bundle springCoreBundle = findBundle("org.springframework.core");
+               update.getConditionalPermissionInfos()
+                               .add(permissionAdmin.newConditionalPermissionInfo(null,
+                                               new ConditionInfo[] { new ConditionInfo(BundleLocationCondition.class.getName(),
+                                                               new String[] { springCoreBundle.getLocation() }) },
+                                               new PermissionInfo[] { new PermissionInfo(RuntimePermission.class.getName(), "*", null),
+                                                               new PermissionInfo(AdminPermission.class.getName(), "*", "*"), },
+                                               ConditionalPermissionInfo.ALLOW));
+               Bundle blueprintIoBundle = findBundle("org.eclipse.gemini.blueprint.io");
+               update.getConditionalPermissionInfos()
+                               .add(permissionAdmin.newConditionalPermissionInfo(null,
+                                               new ConditionInfo[] { new ConditionInfo(BundleLocationCondition.class.getName(),
+                                                               new String[] { blueprintIoBundle.getLocation() }) },
+                                               new PermissionInfo[] { new PermissionInfo(RuntimePermission.class.getName(), "*", null),
+                                                               new PermissionInfo(AdminPermission.class.getName(), "*", "*"), },
+                                               ConditionalPermissionInfo.ALLOW));
+
+               // Equinox
+               Bundle registryBundle = findBundle("org.eclipse.equinox.registry");
+               update.getConditionalPermissionInfos().add(permissionAdmin.newConditionalPermissionInfo(null,
+                               new ConditionInfo[] { new ConditionInfo(BundleLocationCondition.class.getName(),
+                                               new String[] { registryBundle.getLocation() }) },
+                               new PermissionInfo[] { new PermissionInfo(PropertyPermission.class.getName(), "eclipse.*", "read"),
+                                               new PermissionInfo(PropertyPermission.class.getName(), "osgi.*", "read"),
+                                               new PermissionInfo(FilePermission.class.getName(), "<<ALL FILES>>", "read,write,delete"), },
+                               ConditionalPermissionInfo.ALLOW));
+
+               Bundle equinoxUtilBundle = findBundle("org.eclipse.equinox.util");
+               update.getConditionalPermissionInfos().add(permissionAdmin.newConditionalPermissionInfo(null,
+                               new ConditionInfo[] { new ConditionInfo(BundleLocationCondition.class.getName(),
+                                               new String[] { equinoxUtilBundle.getLocation() }) },
+                               new PermissionInfo[] { new PermissionInfo(PropertyPermission.class.getName(), "equinox.*", "read"),
+                                               new PermissionInfo(ServicePermission.class.getName(), "*", "get"),
+                                               new PermissionInfo(ServicePermission.class.getName(), "*", "register"), },
+                               ConditionalPermissionInfo.ALLOW));
+               Bundle equinoxCommonBundle = findBundle("org.eclipse.equinox.common");
+               update.getConditionalPermissionInfos()
+                               .add(permissionAdmin.newConditionalPermissionInfo(null,
+                                               new ConditionInfo[] { new ConditionInfo(BundleLocationCondition.class.getName(),
+                                                               new String[] { equinoxCommonBundle.getLocation() }) },
+                                               new PermissionInfo[] { new PermissionInfo(AdminPermission.class.getName(), "*", "*"), },
+                                               ConditionalPermissionInfo.ALLOW));
+
+               Bundle consoleBundle = findBundle("org.eclipse.equinox.console");
+               update.getConditionalPermissionInfos()
+                               .add(permissionAdmin.newConditionalPermissionInfo(null,
+                                               new ConditionInfo[] { new ConditionInfo(BundleLocationCondition.class.getName(),
+                                                               new String[] { consoleBundle.getLocation() }) },
+                                               new PermissionInfo[] { new PermissionInfo(ServicePermission.class.getName(), "*", "register"),
+                                                               new PermissionInfo(AdminPermission.class.getName(), "*", "listener") },
+                                               ConditionalPermissionInfo.ALLOW));
+               Bundle preferencesBundle = findBundle("org.eclipse.equinox.preferences");
+               update.getConditionalPermissionInfos().add(permissionAdmin.newConditionalPermissionInfo(null,
+                               new ConditionInfo[] { new ConditionInfo(BundleLocationCondition.class.getName(),
+                                               new String[] { preferencesBundle.getLocation() }) },
+                               new PermissionInfo[] {
+                                               new PermissionInfo(FilePermission.class.getName(), "<<ALL FILES>>", "read,write,delete"), },
+                               ConditionalPermissionInfo.ALLOW));
+               Bundle appBundle = findBundle("org.eclipse.equinox.app");
+               update.getConditionalPermissionInfos().add(permissionAdmin.newConditionalPermissionInfo(null,
+                               new ConditionInfo[] { new ConditionInfo(BundleLocationCondition.class.getName(),
+                                               new String[] { appBundle.getLocation() }) },
+                               new PermissionInfo[] {
+                                               new PermissionInfo(FilePermission.class.getName(), "<<ALL FILES>>", "read,write,delete"), },
+                               ConditionalPermissionInfo.ALLOW));
+
+               // Jackrabbit
+               Bundle jackrabbitCoreBundle = findBundle("org.apache.jackrabbit.core");
+               update.getConditionalPermissionInfos().add(permissionAdmin.newConditionalPermissionInfo(null,
+                               new ConditionInfo[] { new ConditionInfo(BundleLocationCondition.class.getName(),
+                                               new String[] { jackrabbitCoreBundle.getLocation() }) },
+                               new PermissionInfo[] {
+                                               new PermissionInfo(FilePermission.class.getName(), "<<ALL FILES>>", "read,write,delete"),
+                                               new PermissionInfo(PropertyPermission.class.getName(), "*", "read,write"),
+                                               new PermissionInfo(AuthPermission.class.getName(), "getLoginConfiguration", null),
+                                               new PermissionInfo(AuthPermission.class.getName(), "createLoginContext.Jackrabbit", null), },
+                               ConditionalPermissionInfo.ALLOW));
+               Bundle jackrabbitCommonBundle = findBundle("org.apache.jackrabbit.jcr.commons");
+               update.getConditionalPermissionInfos().add(permissionAdmin.newConditionalPermissionInfo(null,
+                               new ConditionInfo[] { new ConditionInfo(BundleLocationCondition.class.getName(),
+                                               new String[] { jackrabbitCommonBundle.getLocation() }) },
+                               new PermissionInfo[] {
+                                               new PermissionInfo(AuthPermission.class.getName(), "createLoginContext.Jackrabbit", null), },
+                               ConditionalPermissionInfo.ALLOW));
+               Bundle tikaCoreBundle = findBundle("org.apache.tika.core");
+               update.getConditionalPermissionInfos()
+                               .add(permissionAdmin.newConditionalPermissionInfo(null,
+                                               new ConditionInfo[] { new ConditionInfo(BundleLocationCondition.class.getName(),
+                                                               new String[] { tikaCoreBundle.getLocation() }) },
+                                               new PermissionInfo[] { new PermissionInfo(PropertyPermission.class.getName(), "*", "read"),
+                                                               new PermissionInfo(AdminPermission.class.getName(), "*", "*") },
+                                               ConditionalPermissionInfo.ALLOW));
+               Bundle luceneBundle = findBundle("org.apache.lucene");
+               update.getConditionalPermissionInfos()
+                               .add(permissionAdmin.newConditionalPermissionInfo(null,
+                                               new ConditionInfo[] { new ConditionInfo(BundleLocationCondition.class.getName(),
+                                                               new String[] { luceneBundle.getLocation() }) },
+                                               new PermissionInfo[] {
+                                                               new PermissionInfo(FilePermission.class.getName(), "<<ALL FILES>>",
+                                                                               "read,write,delete"),
+                                                               new PermissionInfo(PropertyPermission.class.getName(), "*", "read"),
+                                                               new PermissionInfo(AdminPermission.class.getName(), "*", "*") },
+                                               ConditionalPermissionInfo.ALLOW));
+
+               // COMMIT
+               update.commit();
+       }
+
+       /** @return bundle location */
+       default String locate(Class<?> clzz) {
+               return FrameworkUtil.getBundle(clzz).getLocation();
+       }
+
+       /** Can be null */
+       default Bundle findBundle(String symbolicName) {
+               for (Bundle b : bc.getBundles())
+                       if (b.getSymbolicName().equals(symbolicName))
+                               return b;
+               return null;
+       }
+
+}
index 3e5e25d041ad8891420aff341149390ea453ae23..8f00c457708d31322bde2b1937a286dc3de78b9e 100644 (file)
@@ -29,9 +29,9 @@ import org.argeo.cms.CmsView;
 import org.argeo.cms.auth.CurrentUser;
 import org.argeo.cms.auth.HttpRequestCallback;
 import org.argeo.cms.i18n.LocaleUtils;
+import org.argeo.cms.internal.auth.LocaleChoice;
 import org.argeo.cms.util.CmsUtils;
 import org.argeo.eclipse.ui.dialogs.ErrorFeedback;
-import org.argeo.util.LocaleChoice;
 import org.eclipse.rap.rwt.RWT;
 import org.eclipse.swt.SWT;
 import org.eclipse.swt.events.MouseAdapter;
index bc99bc48bb8ff362d9e80e2becaac1f2e5dceaa3..55190d39cd9d0e0d7cb0c91bf5ec442589d2a990 100644 (file)
@@ -10,7 +10,7 @@ import javax.security.auth.callback.PasswordCallback;
 import javax.security.auth.callback.TextOutputCallback;
 import javax.security.auth.callback.UnsupportedCallbackException;
 
-import org.argeo.util.LocaleChoice;
+import org.argeo.cms.internal.auth.LocaleChoice;
 import org.eclipse.swt.SWT;
 import org.eclipse.swt.events.KeyEvent;
 import org.eclipse.swt.events.KeyListener;
index 34ac98b1cb0e8f069945fd85d2233251aea5005f..aabc9a0e78a9082ac0897566e83ea6c36b95e443 100644 (file)
@@ -13,6 +13,7 @@ import junit.framework.TestCase;
 
 import org.apache.commons.codec.binary.Base64;
 import org.apache.commons.codec.digest.DigestUtils;
+import org.argeo.util.naming.LdifParser;
 
 public class LdifParserTest extends TestCase implements BasicTestConstants {
        public void testBasicLdif() throws Exception {
diff --git a/org.argeo.security.core/src/org/argeo/osgi/useradmin/LdifParser.java b/org.argeo.security.core/src/org/argeo/osgi/useradmin/LdifParser.java
deleted file mode 100644 (file)
index da793ad..0000000
+++ /dev/null
@@ -1,147 +0,0 @@
-package org.argeo.osgi.useradmin;
-
-import static org.argeo.osgi.useradmin.LdifName.dn;
-
-import java.io.IOException;
-import java.io.InputStream;
-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.codec.binary.Base64;
-import org.apache.commons.io.IOUtils;
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-
-/** Basic LDIF parser. */
-class LdifParser {
-       private final static Log log = LogFactory.getLog(LdifParser.class);
-
-       protected Attributes addAttributes(SortedMap<LdapName, Attributes> 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);
-               }
-       }
-
-       static void checkDnConsistency() {
-
-       }
-
-       SortedMap<LdapName, Attributes> read(InputStream in) throws IOException {
-               SortedMap<LdapName, Attributes> res = new TreeMap<LdapName, Attributes>();
-               try {
-                       List<String> lines = IOUtils.readLines(in);
-                       // 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
-                                                       .decodeBase64(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 {
-                       IOUtils.closeQuietly(in);
-               }
-               return res;
-       }
-}
\ No newline at end of file
index 7b87a4b6e7fbd497cf8786c853cb7616589e8171..fbedeb79c81422917fb035b261aaedd296510a99 100644 (file)
@@ -23,6 +23,8 @@ import javax.naming.ldap.LdapName;
 import javax.transaction.TransactionManager;
 
 import org.apache.commons.io.IOUtils;
+import org.argeo.util.naming.LdifParser;
+import org.argeo.util.naming.LdifWriter;
 import org.osgi.framework.Filter;
 import org.osgi.service.useradmin.Role;
 
diff --git a/org.argeo.security.core/src/org/argeo/osgi/useradmin/LdifWriter.java b/org.argeo.security.core/src/org/argeo/osgi/useradmin/LdifWriter.java
deleted file mode 100644 (file)
index ba393ca..0000000
+++ /dev/null
@@ -1,71 +0,0 @@
-package org.argeo.osgi.useradmin;
-
-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 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.apache.commons.codec.binary.Base64;
-
-/** Basic LDIF writer */
-class LdifWriter {
-       private final Writer writer;
-
-       LdifWriter(OutputStream out) {
-               this.writer = new OutputStreamWriter(out);
-       }
-
-       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<? extends Attribute> 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);
-               }
-       }
-
-       private 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.encodeBase64String((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.core/src/org/argeo/util/naming/AttributesDictionary.java b/org.argeo.security.core/src/org/argeo/util/naming/AttributesDictionary.java
new file mode 100644 (file)
index 0000000..a6fddb4
--- /dev/null
@@ -0,0 +1,162 @@
+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<String, Object> {
+       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<String> keys() {
+               NamingEnumeration<String> namingEnumeration = attributes.getIDs();
+               return new Enumeration<String>() {
+
+                       @Override
+                       public boolean hasMoreElements() {
+                               return namingEnumeration.hasMoreElements();
+                       }
+
+                       @Override
+                       public String nextElement() {
+                               return namingEnumeration.nextElement();
+                       }
+
+               };
+       }
+
+       @Override
+       public Enumeration<Object> elements() {
+               NamingEnumeration<String> namingEnumeration = attributes.getIDs();
+               return new Enumeration<Object>() {
+
+                       @Override
+                       public boolean hasMoreElements() {
+                               return namingEnumeration.hasMoreElements();
+                       }
+
+                       @Override
+                       public Object nextElement() {
+                               String key = namingEnumeration.nextElement();
+                               return get(key);
+                       }
+
+               };
+       }
+
+       @Override
+       /** @returns a <code>String</code> or <code>String[]</code> */
+       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");
+                       attr.set(0, 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 <b>content</b> of an {@link javax.naming.Attributes} to the
+        * provided {@link Dictionary}.
+        */
+       public static void copy(Attributes attributes, Dictionary<String, Object> dictionary) {
+               AttributesDictionary ad = new AttributesDictionary(attributes);
+               Enumeration<String> 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<String, Object> dictionary, Attributes attributes) {
+               AttributesDictionary ad = new AttributesDictionary(attributes);
+               Enumeration<String> 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
new file mode 100644 (file)
index 0000000..66e529e
--- /dev/null
@@ -0,0 +1,144 @@
+package org.argeo.util.naming;
+
+import static org.argeo.osgi.useradmin.LdifName.dn;
+
+import java.io.IOException;
+import java.io.InputStream;
+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.codec.binary.Base64;
+import org.apache.commons.io.IOUtils;
+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<LdapName, Attributes> 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<LdapName, Attributes> read(InputStream in) throws IOException {
+               SortedMap<LdapName, Attributes> res = new TreeMap<LdapName, Attributes>();
+               try {
+                       List<String> lines = IOUtils.readLines(in);
+                       // 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
+                                                       .decodeBase64(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 {
+                       IOUtils.closeQuietly(in);
+               }
+               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
new file mode 100644 (file)
index 0000000..76586c3
--- /dev/null
@@ -0,0 +1,71 @@
+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 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.apache.commons.codec.binary.Base64;
+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<? extends Attribute> 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);
+               }
+       }
+
+       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.encodeBase64String((byte[]) value);
+                               writer.append(attribute.getID()).append("::").append(encoded).append('\n');
+                       } else {
+                               writer.append(attribute.getID()).append(':').append(value.toString()).append('\n');
+                       }
+               }
+       }
+}
index cb2cbfb2f03d7081ce75527f0b3e9c15c83def3a..a3b8e5fdf54451b6e29d887ac5799019e240437b 100644 (file)
@@ -8,7 +8,7 @@
        http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">\r
 \r
        <!-- REFERENCES -->\r
-       <reference id="secureLogger" interface="org.argeo.ArgeoLogger"\r
+       <reference id="secureLogger" interface="org.argeo.node.ArgeoLogger"\r
                cardinality="0..1" />\r
 \r
        <reference id="nodeRepository" interface="javax.jcr.Repository"\r
index e719407c8ce5dcafeb91ea08ce001f1ecef8d4ab..61efc8cb6ba6136e5740dee71549e95de991a604 100644 (file)
@@ -17,7 +17,7 @@ package org.argeo.security.ui.views;
 
 import java.util.ArrayList;
 
-import org.argeo.ArgeoLogger;
+import org.argeo.node.ArgeoLogger;
 import org.argeo.security.ui.SecurityUiPlugin;
 import org.eclipse.jface.resource.JFaceResources;
 import org.eclipse.jface.viewers.LabelProvider;
index fcc8c964509f00cd58196af569dfe819ac42f138..5b12f439a03febaf324cc5c1b03c41de9f0b72af 100644 (file)
@@ -22,7 +22,7 @@ import java.util.Date;
 import java.util.LinkedList;
 import java.util.List;
 
-import org.argeo.ArgeoLogListener;
+import org.argeo.node.ArgeoLogListener;
 import org.eclipse.jface.viewers.ILazyContentProvider;
 import org.eclipse.jface.viewers.TableViewer;
 import org.eclipse.jface.viewers.Viewer;
index c7c47b352d379520973fd6af2fdf8b19edf63d81..f9e197ac05edcefcdf60fb723c8c037b0880a010 100644 (file)
@@ -17,8 +17,8 @@ package org.argeo.security.ui.views;
 
 import java.util.ArrayList;
 
-import org.argeo.ArgeoLogListener;
-import org.argeo.ArgeoLogger;
+import org.argeo.node.ArgeoLogListener;
+import org.argeo.node.ArgeoLogger;
 import org.argeo.security.ui.SecurityUiPlugin;
 import org.eclipse.jface.resource.JFaceResources;
 import org.eclipse.jface.viewers.LabelProvider;
diff --git a/org.argeo.util/src/org/argeo/ArgeoLogListener.java b/org.argeo.util/src/org/argeo/ArgeoLogListener.java
deleted file mode 100644 (file)
index bac8a98..0000000
+++ /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;
-
-/** 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.util/src/org/argeo/ArgeoLogger.java b/org.argeo.util/src/org/argeo/ArgeoLogger.java
deleted file mode 100644 (file)
index 0657c29..0000000
+++ /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;
-
-/**
- * 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.util/src/org/argeo/util/LocaleChoice.java b/org.argeo.util/src/org/argeo/util/LocaleChoice.java
deleted file mode 100644 (file)
index 6609f4c..0000000
+++ /dev/null
@@ -1,132 +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.util;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-import java.util.Locale;
-
-import javax.security.auth.callback.LanguageCallback;
-
-import org.argeo.ArgeoException;
-
-/** Choose in a list of locales. TODO: replace with {@link LanguageCallback} */
-public class LocaleChoice {
-       private final List<Locale> locales;
-
-       private Integer selectedIndex = null;
-       private final Integer defaultIndex;
-
-       public LocaleChoice(List<Locale> locales, Locale defaultLocale) {
-               Integer defaultIndex = null;
-               this.locales = Collections.unmodifiableList(locales);
-               for (int i = 0; i < locales.size(); i++)
-                       if (locales.get(i).equals(defaultLocale))
-                               defaultIndex = i;
-
-               // based on language only
-               if (defaultIndex == null)
-                       for (int i = 0; i < locales.size(); i++)
-                               if (locales.get(i).getLanguage().equals(defaultLocale.getLanguage()))
-                                       defaultIndex = i;
-
-               if (defaultIndex == null)
-                       throw new ArgeoException("Default locale " + defaultLocale + " is not in available locales " + locales);
-               this.defaultIndex = defaultIndex;
-
-               this.selectedIndex = defaultIndex;
-       }
-
-       /**
-        * Convenience constructor based on a comma separated list of iso codes (en,
-        * en_US, fr_CA, etc.). Default selection is default locale.
-        */
-       public LocaleChoice(String locales, Locale defaultLocale) {
-               this(asLocaleList(locales), defaultLocale);
-       }
-
-       public String[] getSupportedLocalesLabels() {
-               String[] labels = new String[locales.size()];
-               for (int i = 0; i < locales.size(); i++) {
-                       Locale locale = locales.get(i);
-                       if (locale.getCountry().equals(""))
-                               labels[i] = locale.getDisplayLanguage(locale) + " [" + locale.getLanguage() + "]";
-                       else
-                               labels[i] = locale.getDisplayLanguage(locale) + " (" + locale.getDisplayCountry(locale) + ") ["
-                                               + locale.getLanguage() + "_" + locale.getCountry() + "]";
-
-               }
-               return labels;
-       }
-
-       public Locale getSelectedLocale() {
-               if (selectedIndex == null)
-                       return null;
-               return locales.get(selectedIndex);
-       }
-
-       public void setSelectedIndex(Integer selectedIndex) {
-               this.selectedIndex = selectedIndex;
-       }
-
-       public Integer getSelectedIndex() {
-               return selectedIndex;
-       }
-
-       public Integer getDefaultIndex() {
-               return defaultIndex;
-       }
-
-       public List<Locale> getLocales() {
-               return locales;
-       }
-
-       public Locale getDefaultLocale() {
-               return locales.get(getDefaultIndex());
-       }
-
-       /** Returns null if argument is null. */
-       public static List<Locale> asLocaleList(Object locales) {
-               if (locales == null)
-                       return null;
-               ArrayList<Locale> availableLocales = new ArrayList<Locale>();
-               String[] codes = locales.toString().split(",");
-               for (int i = 0; i < codes.length; i++) {
-                       String code = codes[i];
-                       // variant not supported
-                       int indexUnd = code.indexOf("_");
-                       Locale locale;
-                       if (indexUnd > 0) {
-                               String language = code.substring(0, indexUnd);
-                               String country = code.substring(indexUnd + 1);
-                               locale = new Locale(language, country);
-                       } else {
-                               locale = new Locale(code);
-                       }
-                       availableLocales.add(locale);
-               }
-               return availableLocales;
-       }
-
-       public static void main(String[] args) {
-               for (String isoL : Locale.getISOLanguages()) {
-                       Locale locale = new Locale(isoL);
-                       System.out.println(isoL + "\t" + locale.getDisplayLanguage() + "\t" + locale.getDisplayLanguage(locale));
-               }
-       }
-
-}