/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (the "License"). You may not use this file except in compliance
* with the License.
*
* You can obtain a copy of the license at
* trunk/opends/resource/legal-notices/OpenDS.LICENSE
* or https://OpenDS.dev.java.net/OpenDS.LICENSE.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at
* trunk/opends/resource/legal-notices/OpenDS.LICENSE. If applicable,
* add the following below this CDDL HEADER, with the fields enclosed
* by brackets "[]" replaced with your own identifying information:
* Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*
*
* Copyright 2009 Sun Microsystems, Inc.
* Portions Copyright 2013 ForgeRock AS.
*/
package org.opends.guitools.controlpanel.task;
import static org.opends.messages.AdminToolMessages.*;
import java.io.File;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import javax.naming.NamingException;
import javax.naming.directory.BasicAttribute;
import javax.naming.directory.DirContext;
import javax.naming.directory.ModificationItem;
import javax.swing.SwingUtilities;
import org.opends.guitools.controlpanel.datamodel.ControlPanelInfo;
import org.opends.guitools.controlpanel.ui.ColorAndFontConstants;
import org.opends.guitools.controlpanel.ui.ProgressDialog;
import org.opends.guitools.controlpanel.util.Utilities;
import org.opends.messages.Message;
import org.opends.quicksetup.util.Utils;
import org.opends.server.config.ConfigConstants;
import org.opends.server.core.DirectoryServer;
import org.opends.server.types.AttributeType;
import org.opends.server.types.Attributes;
import org.opends.server.types.CommonSchemaElements;
import org.opends.server.types.Entry;
import org.opends.server.types.ExistingFileBehavior;
import org.opends.server.types.LDIFExportConfig;
import org.opends.server.types.LDIFImportConfig;
import org.opends.server.types.Modification;
import org.opends.server.types.ModificationType;
import org.opends.server.types.ObjectClass;
import org.opends.server.types.OpenDsException;
import org.opends.server.util.LDIFReader;
import org.opends.server.util.LDIFWriter;
import org.opends.server.util.ServerConstants;
import org.opends.server.util.StaticUtils;
/**
* An abstract class used to re-factor some code between the different tasks
* that create elements in the schema.
*
*/
public class NewSchemaElementsTask extends Task
{
LinkedHashSet<ObjectClass> ocsToAdd = new LinkedHashSet<ObjectClass>();
LinkedHashSet<AttributeType> attrsToAdd = new LinkedHashSet<AttributeType>();
/**
* Constructor of the task.
* @param info the control panel information.
* @param dlg the progress dialog where the task progress will be displayed.
* @param ocsToAdd the object classes that must be created in order.
* @param attrsToAdd the attributes that must be created in order.
*/
public NewSchemaElementsTask(ControlPanelInfo info, ProgressDialog dlg,
LinkedHashSet<ObjectClass> ocsToAdd,
LinkedHashSet<AttributeType> attrsToAdd)
{
super(info, dlg);
this.ocsToAdd.addAll(ocsToAdd);
this.attrsToAdd.addAll(attrsToAdd);
}
/**
* {@inheritDoc}
*/
public Set<String> getBackends()
{
return Collections.emptySet();
}
/**
* {@inheritDoc}
*/
public boolean canLaunch(Task taskToBeLaunched,
Collection<Message> incompatibilityReasons)
{
boolean canLaunch = true;
if (state == State.RUNNING &&
(taskToBeLaunched.getType() == Task.Type.DELETE_SCHEMA_ELEMENT ||
taskToBeLaunched.getType() == Task.Type.MODIFY_SCHEMA_ELEMENT ||
taskToBeLaunched.getType() == Task.Type.NEW_SCHEMA_ELEMENT))
{
incompatibilityReasons.add(getIncompatibilityMessage(this,
taskToBeLaunched));
canLaunch = false;
}
return canLaunch;
}
/**
* {@inheritDoc}
*/
public void runTask()
{
state = State.RUNNING;
lastException = null;
try
{
updateSchema();
state = State.FINISHED_SUCCESSFULLY;
}
catch (Throwable t)
{
lastException = t;
state = State.FINISHED_WITH_ERROR;
}
}
/**
* {@inheritDoc}
*/
public Type getType()
{
return Type.NEW_SCHEMA_ELEMENT;
}
/**
* {@inheritDoc}
*/
public Message getTaskDescription()
{
if (attrsToAdd.size() == 1 && ocsToAdd.isEmpty())
{
String attributeName = attrsToAdd.iterator().next().getNameOrOID();
return INFO_CTRL_PANEL_NEW_ATTRIBUTE_TASK_DESCRIPTION.get(attributeName);
}
else if (ocsToAdd.size() == 1 && attrsToAdd.isEmpty())
{
String ocName = ocsToAdd.iterator().next().getNameOrOID();
return INFO_CTRL_PANEL_NEW_OBJECTCLASS_TASK_DESCRIPTION.get(ocName);
}
else
{
ArrayList<String> attrNames = new ArrayList<String>();
for (AttributeType attribute : attrsToAdd)
{
attrNames.add(attribute.getNameOrOID());
}
ArrayList<String> ocNames = new ArrayList<String>();
for (ObjectClass oc : ocsToAdd)
{
ocNames.add(oc.getNameOrOID());
}
if (ocNames.isEmpty())
{
return INFO_CTRL_PANEL_NEW_ATTRIBUTES_TASK_DESCRIPTION.get(
Utils.getStringFromCollection(attrNames, ", "));
}
else if (attrNames.isEmpty())
{
return INFO_CTRL_PANEL_NEW_OBJECTCLASSES_TASK_DESCRIPTION.get(
Utils.getStringFromCollection(ocNames, ", "));
}
else
{
return INFO_CTRL_PANEL_NEW_SCHEMA_ELEMENTS_TASK_DESCRIPTION.get(
Utils.getStringFromCollection(attrNames, ", "),
Utils.getStringFromCollection(ocNames, ", "));
}
}
}
/**
* Update the schema.
* @throws OpenDsException if an error occurs.
*/
private void updateSchema() throws OpenDsException
{
if (isServerRunning())
{
updateSchemaOnline();
}
else
{
updateSchemaOffline();
}
}
/**
* {@inheritDoc}
*/
protected String getCommandLinePath()
{
return null;
}
/**
* {@inheritDoc}
*/
protected List<String> getCommandLineArguments()
{
return Collections.emptyList();
}
private void updateSchemaOnline() throws OpenDsException
{
// Add the schema elements one by one: we are not sure that the server
// will handle the adds sequentially if we only send one modification.
for (AttributeType attr : attrsToAdd)
{
addAttributeOnline(attr);
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
getProgressDialog().appendProgressHtml(Utilities.applyFont("<br><br>",
ColorAndFontConstants.progressFont));
}
});
}
for (ObjectClass oc : ocsToAdd)
{
addObjectClassOnline(oc);
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
getProgressDialog().appendProgressHtml(Utilities.applyFont("<br><br>",
ColorAndFontConstants.progressFont));
}
});
}
}
private void updateSchemaOffline() throws OpenDsException
{
// Group the changes in the same schema file.
LinkedHashMap<String, List<AttributeType>> hmAttrs =
new LinkedHashMap<String, List<AttributeType>>();
for (AttributeType attr : attrsToAdd)
{
String fileName = getFileName(attr);
if (fileName == null)
{
fileName = "";
}
List<AttributeType> attrs = hmAttrs.get(fileName);
if (attrs == null)
{
attrs = new ArrayList<AttributeType>();
hmAttrs.put(fileName, attrs);
}
attrs.add(attr);
}
LinkedHashMap<String, List<ObjectClass>> hmOcs =
new LinkedHashMap<String, List<ObjectClass>>();
for (ObjectClass oc : ocsToAdd)
{
String fileName = getFileName(oc);
if (fileName == null)
{
fileName = "";
}
List<ObjectClass> ocs = hmOcs.get(fileName);
if (ocs == null)
{
ocs = new ArrayList<ObjectClass>();
hmOcs.put(fileName, ocs);
}
ocs.add(oc);
}
LinkedHashSet<String> allFileNames = new LinkedHashSet<String>();
allFileNames.addAll(hmAttrs.keySet());
allFileNames.addAll(hmOcs.keySet());
for (String fileName : allFileNames)
{
List<AttributeType> attrs = hmAttrs.get(fileName);
List<ObjectClass> ocs = hmOcs.get(fileName);
if (attrs == null)
{
attrs = Collections.emptyList();
}
if (ocs == null)
{
ocs = Collections.emptyList();
}
if (fileName.equals(""))
{
fileName = null;
}
updateSchemaOffline(fileName, attrs, ocs);
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
getProgressDialog().appendProgressHtml(Utilities.applyFont("<br><br>",
ColorAndFontConstants.progressFont));
}
});
}
}
private void addAttributeOnline(final AttributeType attribute)
throws OpenDsException
{
SwingUtilities.invokeLater(new Runnable()
{
/**
* {@inheritDoc}
*/
public void run()
{
printEquivalentCommandLineToAddOnline(attribute);
getProgressDialog().appendProgressHtml(
Utilities.getProgressWithPoints(
INFO_CTRL_PANEL_CREATING_ATTRIBUTE_PROGRESS.get(
attribute.getNameOrOID()),
ColorAndFontConstants.progressFont));
}
});
try
{
BasicAttribute attr = new BasicAttribute(getAttributeName(attribute));
attr.add(getValueOnline(attribute));
ModificationItem mod = new ModificationItem(DirContext.ADD_ATTRIBUTE,
attr);
getInfo().getDirContext().modifyAttributes(
ConfigConstants.DN_DEFAULT_SCHEMA_ROOT,
new ModificationItem[] { mod });
}
catch (NamingException ne)
{
throw new OnlineUpdateException(
ERR_CTRL_PANEL_ERROR_UPDATING_SCHEMA.get(ne.toString()), ne);
}
notifyConfigurationElementCreated(attribute);
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
getProgressDialog().appendProgressHtml(
Utilities.getProgressDone(ColorAndFontConstants.progressFont));
}
});
}
private void addObjectClassOnline(final ObjectClass objectClass)
throws OpenDsException
{
SwingUtilities.invokeLater(new Runnable()
{
/**
* {@inheritDoc}
*/
public void run()
{
printEquivalentCommandLineToAddOnline(objectClass);
getProgressDialog().appendProgressHtml(
Utilities.getProgressWithPoints(
INFO_CTRL_PANEL_CREATING_OBJECTCLASS_PROGRESS.get(
objectClass.getNameOrOID()),
ColorAndFontConstants.progressFont));
}
});
try
{
BasicAttribute attr = new BasicAttribute(getAttributeName(objectClass));
attr.add(getValueOnline(objectClass));
ModificationItem mod = new ModificationItem(DirContext.ADD_ATTRIBUTE,
attr);
getInfo().getDirContext().modifyAttributes(
ConfigConstants.DN_DEFAULT_SCHEMA_ROOT,
new ModificationItem[] { mod });
}
catch (NamingException ne)
{
throw new OnlineUpdateException(
ERR_CTRL_PANEL_ERROR_UPDATING_SCHEMA.get(ne.toString()), ne);
}
notifyConfigurationElementCreated(objectClass);
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
getProgressDialog().appendProgressHtml(
Utilities.getProgressDone(ColorAndFontConstants.progressFont));
}
});
}
private String getValueOnline(CommonSchemaElements element)
{
return element.toString();
}
private String getValueOffline(CommonSchemaElements element)
{
Iterable<String> previousValues =
element.getExtraProperty(ServerConstants.SCHEMA_PROPERTY_FILENAME);
element.setExtraProperty(ServerConstants.SCHEMA_PROPERTY_FILENAME,
(String)null);
String attributeWithoutFileDefinition = element.toString();
if (previousValues != null)
{
ArrayList<String> vs = new ArrayList<String>();
for (String s : previousValues)
{
vs.add(s);
}
if (!vs.isEmpty())
{
element.setExtraProperty(ServerConstants.SCHEMA_PROPERTY_FILENAME, vs);
}
}
return attributeWithoutFileDefinition;
}
private void printEquivalentCommandLineToAddOnline(
CommonSchemaElements element)
{
ArrayList<String> args = new ArrayList<String>();
args.add("-a");
args.addAll(getObfuscatedCommandLineArguments(
getConnectionCommandLineArguments(true, true)));
args.add(getNoPropertiesFileArgument());
String equiv = getEquivalentCommandLine(getCommandLinePath("ldapmodify"),
args);
StringBuilder sb = new StringBuilder();
Message msg;
if (element instanceof AttributeType)
{
msg = INFO_CTRL_PANEL_EQUIVALENT_CMD_TO_ADD_ATTRIBUTE_ONLINE.get(
element.getNameOrOID());
}
else
{
msg = INFO_CTRL_PANEL_EQUIVALENT_CMD_TO_ADD_OBJECTCLASS_ONLINE.get(
element.getNameOrOID());
}
sb.append(msg).append("<br><b>");
sb.append(equiv);
sb.append("<br>");
sb.append("dn: cn=schema<br>");
sb.append("changetype: modify<br>");
sb.append("add: ").append(getAttributeName(element)).append("<br>");
sb.append(getAttributeName(element)).append(": ")
.append(getValueOnline(element));
sb.append("</b><br><br>");
getProgressDialog().appendProgressHtml(Utilities.applyFont(sb.toString(),
ColorAndFontConstants.progressFont));
}
private String getAttributeName(CommonSchemaElements element)
{
if (element instanceof AttributeType)
{
return ConfigConstants.ATTR_ATTRIBUTE_TYPES;
}
else
{
return ConfigConstants.ATTR_OBJECTCLASSES;
}
}
private String getFileName(CommonSchemaElements element)
{
String value = null;
Iterable<String> vs =
element.getExtraProperty(ServerConstants.SCHEMA_PROPERTY_FILENAME);
if (vs != null)
{
if (vs.iterator().hasNext())
{
value = vs.iterator().next();
}
}
return value;
}
private void updateSchemaOffline(String file,
final List<AttributeType> attributes,
final List<ObjectClass> objectClasses) throws OpenDsException
{
if (file == null)
{
file = ConfigConstants.FILE_USER_SCHEMA_ELEMENTS;
}
File f = new File(file);
if (!f.isAbsolute())
{
f = new File(
DirectoryServer.getEnvironmentConfig().getSchemaDirectory(),
file);
}
final String fileName = f.getAbsolutePath();
final boolean isSchemaFileDefined = isSchemaFileDefined(fileName);
SwingUtilities.invokeLater(new Runnable()
{
/**
* {@inheritDoc}
*/
public void run()
{
printEquivalentCommandToAddOffline(fileName, isSchemaFileDefined,
attributes, objectClasses);
if (attributes.size() == 1 && objectClasses.isEmpty())
{
String attributeName = attributes.get(0).getNameOrOID();
getProgressDialog().appendProgressHtml(
Utilities.getProgressWithPoints(
INFO_CTRL_PANEL_CREATING_ATTRIBUTE_PROGRESS.get(
attributeName),
ColorAndFontConstants.progressFont));
}
else if (objectClasses.size() == 1 && attributes.isEmpty())
{
String ocName = objectClasses.get(0).getNameOrOID();
getProgressDialog().appendProgressHtml(
Utilities.getProgressWithPoints(
INFO_CTRL_PANEL_CREATING_OBJECTCLASS_PROGRESS.get(
ocName),
ColorAndFontConstants.progressFont));
}
else
{
getProgressDialog().appendProgressHtml(
Utilities.getProgressWithPoints(
INFO_CTRL_PANEL_UPDATING_SCHEMA_FILE_PROGRESS.get(
fileName),
ColorAndFontConstants.progressFont));
}
}
});
updateSchemaFile(fileName, isSchemaFileDefined, attributes, objectClasses);
for (AttributeType attr : attributes)
{
notifyConfigurationElementCreated(attr);
}
for (ObjectClass oc : objectClasses)
{
notifyConfigurationElementCreated(oc);
}
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
getProgressDialog().appendProgressHtml(
Utilities.getProgressDone(ColorAndFontConstants.progressFont));
}
});
}
private void printEquivalentCommandToAddOffline(String schemaFile,
boolean isSchemaFileDefined,
List<AttributeType> attributes,
List<ObjectClass> objectClasses)
{
ArrayList<String> names = new ArrayList<String>();
for (AttributeType attr : attributes)
{
names.add(attr.getNameOrOID());
}
for (ObjectClass oc : objectClasses)
{
names.add(oc.getNameOrOID());
}
if (isSchemaFileDefined)
{
StringBuilder sb = new StringBuilder();
sb.append(
INFO_CTRL_PANEL_EQUIVALENT_CMD_TO_ADD_SCHEMA_ELEMENT_OFFLINE.get(
Utils.getStringFromCollection(names, ", "),
schemaFile))
.append("<br><b>");
for (AttributeType attribute : attributes)
{
sb.append(getAttributeName(attribute)).append(": ")
.append(getValueOffline(attribute)).append("<br>");
}
for (ObjectClass oc : objectClasses)
{
sb.append(getAttributeName(oc)).append(": ")
.append(getValueOffline(oc)).append("<br>");
}
sb.append("</b><br><br>");
getProgressDialog().appendProgressHtml(Utilities.applyFont(sb.toString(),
ColorAndFontConstants.progressFont));
}
else
{
StringBuilder sb = new StringBuilder();
sb.append(INFO_CTRL_PANEL_EQUIVALENT_CMD_TO_ADD_SCHEMA_ENTRY_OFFLINE.get(
Utils.getStringFromCollection(names, ", "),
schemaFile)).append("<br><b>");
for (String line : getSchemaEntryLines())
{
sb.append(line);
sb.append("<br>");
}
for (AttributeType attribute : attributes)
{
sb.append(getAttributeName(attribute)).append(": ")
.append(getValueOffline(attribute)).append("<br>");
}
for (ObjectClass oc : objectClasses)
{
sb.append(getAttributeName(oc)).append(": ")
.append(getValueOffline(oc)).append("<br>");
}
sb.append("</b><br><br>");
getProgressDialog().appendProgressHtml(Utilities.applyFont(sb.toString(),
ColorAndFontConstants.progressFont));
}
}
/**
* Returns whether the file defined in the schema element exists or not.
* @param schemaFile the path to the schema file.
* @return <CODE>true</CODE> if the schema file is defined and
* <CODE>false</CODE> otherwise.
*/
private boolean isSchemaFileDefined(String schemaFile)
{
boolean schemaDefined = false;
LDIFReader reader = null;
try
{
reader = new LDIFReader(new LDIFImportConfig(schemaFile));
Entry entry = reader.readEntry();
if (entry != null)
{
schemaDefined = true;
}
}
catch (Throwable t)
{
}
finally
{
StaticUtils.close(reader);
}
return schemaDefined;
}
/**
* Returns the list of LDIF lines that are enough to create the entry
* containing only the schema element associated with this task.
* @return the list of LDIF lines that are enough to create the entry
* containing only the schema element associated with this task.
*/
private ArrayList<String> getSchemaEntryLines()
{
ArrayList<String> lines = new ArrayList<String>();
lines.add("dn: cn=schema");
lines.add("objectClass: top");
lines.add("objectClass: ldapSubentry");
lines.add("objectClass: subschema");
return lines;
}
/**
* Updates the contents of the schema file.
*
* @param schemaFile the schema file.
* @param isSchemaFileDefined whether the schema is defined or not.
* @param attributes the attributes to add.
* @param objectClasses the object classes to add.
* @throws OpenDsException if an error occurs updating the schema file.
*/
private void updateSchemaFile(String schemaFile,
boolean isSchemaFileDefined,
List<AttributeType> attributes,
List<ObjectClass> objectClasses) throws OpenDsException
{
if (isSchemaFileDefined)
{
LDIFExportConfig exportConfig =
new LDIFExportConfig(schemaFile,
ExistingFileBehavior.OVERWRITE);
LDIFReader reader = null;
LDIFWriter writer = null;
try
{
reader = new LDIFReader(new LDIFImportConfig(schemaFile));
Entry schemaEntry = reader.readEntry();
for (AttributeType attribute : attributes)
{
Modification mod = new Modification(ModificationType.ADD,
Attributes.create(getAttributeName(attribute).toLowerCase(),
getValueOffline(attribute)));
schemaEntry.applyModification(mod);
}
for (ObjectClass oc : objectClasses)
{
Modification mod = new Modification(ModificationType.ADD,
Attributes.create(getAttributeName(oc).toLowerCase(),
getValueOffline(oc)));
schemaEntry.applyModification(mod);
}
writer = new LDIFWriter(exportConfig);
writer.writeEntry(schemaEntry);
exportConfig.getWriter().newLine();
}
catch (Throwable t)
{
throw new OfflineUpdateException(
ERR_CTRL_PANEL_ERROR_UPDATING_SCHEMA.get(t.toString()), t);
}
finally
{
StaticUtils.close(reader, exportConfig, writer);
}
}
else
{
LDIFExportConfig exportConfig =
new LDIFExportConfig(schemaFile,
ExistingFileBehavior.FAIL);
try
{
ArrayList<String> lines = getSchemaEntryLines();
for (AttributeType attribute : attributes)
{
lines.add(
getAttributeName(attribute)+": "+getValueOffline(attribute));
}
for (ObjectClass oc : objectClasses)
{
lines.add(getAttributeName(oc)+": "+getValueOffline(oc));
}
for (String line : lines)
{
LDIFWriter.writeLDIFLine(new StringBuilder(line),
exportConfig.getWriter(), exportConfig.getWrapColumn() > 1,
exportConfig.getWrapColumn());
}
exportConfig.getWriter().newLine();
}
catch (Throwable t)
{
throw new OfflineUpdateException(
ERR_CTRL_PANEL_ERROR_UPDATING_SCHEMA.get(t.toString()), t);
}
finally
{
StaticUtils.close(exportConfig);
}
}
}
}