Java tutorial
/** * Copyright (c) 2009--2014 Red Hat, Inc. * * This software is licensed to you under the GNU General Public License, * version 2 (GPLv2). There is NO WARRANTY for this software, express or * implied, including the implied warranties of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. You should have received a copy of GPLv2 * along with this software; if not, see * http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. * * Red Hat trademarks are not licensed under GPLv2. No permission is * granted to use or replicate Red Hat trademarks that are incorporated * in this software or its documentation. */ package com.redhat.rhn.domain.config; import com.redhat.rhn.common.db.datasource.CallableMode; import com.redhat.rhn.common.db.datasource.DataResult; import com.redhat.rhn.common.db.datasource.ModeFactory; import com.redhat.rhn.common.db.datasource.SelectMode; import com.redhat.rhn.common.hibernate.HibernateFactory; import com.redhat.rhn.common.localization.LocalizationService; import com.redhat.rhn.common.util.SHA256Crypt; import com.redhat.rhn.domain.common.Checksum; import com.redhat.rhn.domain.common.ChecksumFactory; import com.redhat.rhn.domain.org.Org; import com.redhat.rhn.domain.server.Server; import com.redhat.rhn.domain.user.User; import org.apache.commons.lang.StringUtils; import org.apache.log4j.Logger; import org.hibernate.ObjectNotFoundException; import org.hibernate.Query; import org.hibernate.Session; import org.hibernate.criterion.Restrictions; import java.io.IOException; import java.io.InputStream; import java.sql.Types; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; /** * ConfigurationFactory. For use when dealing with ConfigChannel, ConfigChannelType, * ConfigFile, ConfigRevision, ConfigFileState, ConfigContent, and ConfigInfo. * * When saving config channels, config files, and config revisions: please use the * commitConfigBlah methods. * @version $Rev$ */ public class ConfigurationFactory extends HibernateFactory { private static ConfigurationFactory singleton = new ConfigurationFactory(); private static Logger log = Logger.getLogger(ConfigurationFactory.class); private ConfigurationFactory() { super(); } @Override protected Logger getLogger() { return log; } /** * Create a new ConfigFile object. * @return new ConfigFile object */ public static ConfigFile newConfigFile() { return new ConfigFile(); } /** * Create a new ConfigRevision object. * @return new ConfigRevision object */ public static ConfigRevision newConfigRevision() { ConfigRevision cr = new ConfigRevision(); Date now = new Date(); cr.setRevision(new Long(1)); cr.setCreated(now); cr.setModified(now); return cr; } /** * Create a new ConfigChannel object. * @return new ConfigChannel object */ public static ConfigChannel newConfigChannel() { return new ConfigChannel(); } /** * Create a new ConfigContent object. * @return new ConfigContent object */ public static ConfigContent newConfigContent() { return new ConfigContent(); } /** * Create and save a new configuration channel. * This method creates a configuration channel and then uses the * saveNewConfigChannel(ConfigChannel) method to save it. * @param org The org for this configuration channel. * @param type The type. Please use the constants located in this class. * @param name The name of this configuration channel. * @param label The label for this configuration channel. * @param description The description of this configuration channel. * @return The newly saved configuration channel. */ public static ConfigChannel saveNewConfigChannel(Org org, ConfigChannelType type, String name, String label, String description) { ConfigChannel out = new ConfigChannel(); out.setOrg(org); out.setName(name); out.setLabel(label); out.setDescription(description); out.setConfigChannelType(type); saveNewConfigChannel(out); return out; } /** * Save a new configuration channel. * Note, this method uses a stored procedure, so it must be used for all newly * created configuration channels. * @param channel The channel object to persist. */ public static void saveNewConfigChannel(ConfigChannel channel) { CallableMode m = ModeFactory.getCallableMode("config_queries", "create_new_config_channel"); Map inParams = new HashMap(); Map outParams = new HashMap(); inParams.put("org_id_in", channel.getOrgId()); inParams.put("type_in", channel.getConfigChannelType().getLabel()); inParams.put("name_in", channel.getName()); inParams.put("label_in", channel.getLabel()); inParams.put("description_in", channel.getDescription()); //Outparam outParams.put("channelId", new Integer(Types.NUMERIC)); Map result = m.execute(inParams, outParams); Long channelId = (Long) result.get("channelId"); channel.setId(channelId); } /** * Save a new configuration file. * Note, this method uses a stored procedure, so it must be used for all newly * created configuration files. * NOTE: This configuration file must have a persisted configuration channel * attached to it. config channels also used stored procedures for * insertions, so we can't simply ask hibernate to save it for us. * @param file The configuration file to persist * @return config file id */ public static Long saveNewConfigFile(ConfigFile file) { //This is designed to catch some of the cases in which the config channel //was not saved before the config file. //There is still the possibility that the config channel hasn't been committed to //the database yet, but someone has set its id. This should never happen from the //web site, but it might happen from tests. if (file.getConfigChannel() == null || file.getConfigChannel().getId() == null) { throw new IllegalStateException("Config Channels must be " + "saved before config files"); } //Have to commit the configFileName before we commit the // ConfigFile so the stored proc will have an ID to work with singleton.saveObject(file.getConfigFileName()); CallableMode m = ModeFactory.getCallableMode("config_queries", "create_new_config_file"); Map inParams = new HashMap(); Map outParams = new HashMap(); //this will generate a foreign-key constraint violation if the config //channel is not already persisted. inParams.put("config_channel_id_in", file.getConfigChannel().getId()); inParams.put("name_in", file.getConfigFileName().getPath()); // Outparam outParams.put("configFileId", new Integer(Types.NUMERIC)); Map result = m.execute(inParams, outParams); return (Long) result.get("configFileId"); } /** * Save a new ConfigRevision. * Note, this method uses a stored procedure, so it must be used for all newly * created configuration revisions. * NOTE: This configuration revision must have a persisted config file * attached to it. config files also used stored procedures for * insertions, so we can't simply ask hibernate to save it for us. * @param revision the new ConfigRevision we want to store. * @return returns revision id */ public static Long saveNewConfigRevision(ConfigRevision revision) { //This is designed to catch some of the cases in which the config file //was not saved before the config revision. //There is still the possibility that the config file hasn't been committed to //the database yet, but someone has set its id. This should never happen from the //web site, but it might happen from tests. if (revision.getConfigFile() == null || revision.getConfigFile().getId() == null) { throw new IllegalStateException("Config Channels must be " + "saved before config files"); } CallableMode m = ModeFactory.getCallableMode("config_queries", "create_new_config_revision"); if (revision.isFile()) { //We need to save the content first so that we have an id for // the stored procedure. singleton.saveObject(revision.getConfigContent()); } //We do not have to save the ConfigInfo, because the info should always already be // in the database. If this is not the case, please read the documentation for // lookupOrInsertConfigInfo(String, String, Long) and correct the problem. Map inParams = new HashMap(); Map outParams = new HashMap(); inParams.put("revision_in", revision.getRevision()); inParams.put("config_file_id_in", revision.getConfigFile().getId()); if (revision.isFile()) { inParams.put("config_content_id_in", revision.getConfigContent().getId()); } else { inParams.put("config_content_id_in", null); } inParams.put("config_info_id_in", revision.getConfigInfo().getId()); inParams.put("config_file_type_id", new Long(revision.getConfigFileType().getId())); // Outparam outParams.put("configRevisionId", new Integer(Types.NUMERIC)); Map result = m.execute(inParams, outParams); return (Long) result.get("configRevisionId"); } private static void save(ConfigChannel channel) { singleton.saveObject(channel); } private static void save(ConfigFile file) { singleton.saveObject(file); } private static void save(ConfigRevision revision) { singleton.saveObject(revision); } /** * Save or update a config channel. Since config channels * use a stored procedure for inserting, we have to decide whether to * insert or update here. If the channel's id is null, we insert. * @param channel The channel to save or update */ public static void commit(ConfigChannel channel) { if (channel.getId() == null) { saveNewConfigChannel(channel); } else { save(channel); } } /** * Save or update a config file. Since config files * use a stored procedure for inserting, we have to decide whether to * insert or update here. If the file's id is null, we insert. * @param file The file to save or update * @return config file */ public static ConfigFile commit(ConfigFile file) { commit(file.getConfigChannel()); if (file.getId() == null) { Long fileId = saveNewConfigFile(file); file = (ConfigFile) getSession().get(ConfigFile.class, fileId); } else { save(file); } return file; } /** * Save or update a config revision. Since config revisions * use a stored procedure for inserting, we have to decide whether to * insert or update here. If the revision's id is null, we insert. * @param revision The revision to save or update * @return returns config revision (with set id) */ public static ConfigRevision commit(ConfigRevision revision) { ConfigFile file = revision.getConfigFile(); commit(file); if (revision.getId() == null) { // save changedById, because saveNewConfigRevision does not handle it // and set it after reload not to lose it Long changedById = revision.getChangedById(); Long revId = saveNewConfigRevision(revision); revision = (ConfigRevision) getSession().get(ConfigRevision.class, revId); revision.setChangedById(changedById); file.setLatestConfigRevision(revision); //and now we have to save the file again //it would be nice to save it only once, but we require the file id //in order to save the revision and the latestConfigRevision is the //revision id. commit(file); } else { //ConfigInfos have a unique constraint for their four data fields. //The config info object associated with this revision may have been //changed, so we need to carefully not update the database record. String targetPath = revision.getConfigInfo().getTargetFileName() == null ? null : revision.getConfigInfo().getTargetFileName().getPath(); ConfigInfo info = lookupOrInsertConfigInfo(revision.getConfigInfo().getUsername(), revision.getConfigInfo().getGroupname(), revision.getConfigInfo().getFilemode(), revision.getConfigInfo().getSelinuxCtx(), targetPath); //if the object did not change, we now have two hibernate objects //with the same identifier. Evict one so that hibernate doesn't get mad. getSession().evict(revision.getConfigInfo()); revision.setConfigInfo(info); } // And now, because saveNewConfigRevision doesn't store -every-thing // about a revision, we have to commit it -again-. Sigh. See BZ212236 save(revision); return revision; } /** * Lookup a ConfigChannel by its id * @param id The identifier for the ConfigChannel * @return the ConfigChannel found or null if not found. */ public static ConfigChannel lookupConfigChannelById(Long id) { Session session = HibernateFactory.getSession(); ConfigChannel c = (ConfigChannel) session.get(ConfigChannel.class, id); return c; } /** * Lookup a ConfigChannel by its label. A config channel * is uniquely identified by label, org id and channel type * @param label The label for the ConfigChannel * @param org the org to which the config channel belongs. * @param cct the config channel type of the config channel. * @return the ConfigChannel found or null if not found. */ public static ConfigChannel lookupConfigChannelByLabel(String label, Org org, ConfigChannelType cct) { Session session = HibernateFactory.getSession(); ConfigChannel c = (ConfigChannel) session.createCriteria(ConfigChannel.class) .add(Restrictions.eq("org", org)).add(Restrictions.eq("label", label)) .add(Restrictions.eq("configChannelType", cct)).uniqueResult(); return c; } /** * Lookup a ConfigFile by its id * @param id The identifier for the ConfigFile * @return the ConfigFile found or null if not found. */ public static ConfigFile lookupConfigFileById(Long id) { Session session = HibernateFactory.getSession(); ConfigFile c = (ConfigFile) session.get(ConfigFile.class, id); return c; } /** * Lookup a ConfigFile by its channel's id and config file name's id * @param channel The file's config channel id * @param name The file's config file name id * @return the ConfigFile found or null if not found. */ public static ConfigFile lookupConfigFileByChannelAndName(Long channel, Long name) { Session session = HibernateFactory.getSession(); Query query = session.getNamedQuery("ConfigFile.findByChannelAndName") .setLong("channel_id", channel.longValue()).setLong("name_id", name.longValue()) .setLong("state_id", ConfigFileState.normal().getId().longValue()) //Retrieve from cache if there .setCacheable(true); try { return (ConfigFile) query.uniqueResult(); } catch (ObjectNotFoundException e) { return null; } } /** * Finds a ConfigRevision from the database with a given id. * @param id The identifier for the ConfigRevision * @return The sought for ConfigRevision or null if not found. */ public static ConfigRevision lookupConfigRevisionById(Long id) { Session session = HibernateFactory.getSession(); ConfigRevision a = (ConfigRevision) session.get(ConfigRevision.class, id); return a; } /** * Finds a ConfigRevision for a given ConfigFile and given revision id * @param cf The ConfigFile to look for. * @param revId The ConfigFile revision to look for. * @return ConfigRevision The sought for ConfigRevision. */ public static ConfigRevision lookupConfigRevisionByRevId(ConfigFile cf, Long revId) { Session session = HibernateFactory.getSession(); Query q = session.getNamedQuery("ConfigRevision.findByRevisionAndConfigFile"); q.setLong("rev", revId.longValue()); q.setEntity("cf", cf); return (ConfigRevision) q.uniqueResult(); } /** * Finds configuration revisions for a given configuration file * @param cf The ConfigFile to look for. * @return List of configuration revisions for given configuration file. */ public static List lookupConfigRevisions(ConfigFile cf) { Session session = HibernateFactory.getSession(); Query q = session.getNamedQuery("ConfigRevision.findByConfigFile"); q.setEntity("cf", cf); return q.list(); } /** * Finds a ConfigInfo from the database with a given id. * @param id The identifier for the ConfigInfo * @return The sought for ConfigInfo or null if not found. */ public static ConfigInfo lookupConfigInfoById(Long id) { Session session = HibernateFactory.getSession(); ConfigInfo c = (ConfigInfo) session.get(ConfigInfo.class, id); return c; } /** * Finds a ConfigFileName from the database with a given id. * @param id The identifier for the ConfigFileName * @return The sought for ConfigFileName or null if not found. */ public static ConfigFileName lookupConfigFileNameById(Long id) { Session session = HibernateFactory.getSession(); ConfigFileName c = (ConfigFileName) session.get(ConfigFileName.class, id); return c; } /** * Used to look up ConfigChannelTypes. Note: there is a static list of * ConfigChannelTypes and therefore a static list of labels. This method * is private because there are public static member variables for each * ConfigChannelType * @param label The unique label of the type. * @return A sought for ConfigChannelType or null */ static ConfigChannelType lookupConfigChannelTypeByLabel(String label) { Session session = HibernateFactory.getSession(); return (ConfigChannelType) session.getNamedQuery("ConfigChannelType.findByLabel").setString("label", label) //Retrieve from cache if there .setCacheable(true).uniqueResult(); } /** * Used to look up ConfigFileStates. Note: there is a static list of * ConfigFileStates and therefore a static list of labels. This method * is private because there are public static member variables for each * ConfigChannelType * @param label The unique label of the type. * @return A sought for ConfigFileState or null */ static ConfigFileState lookupConfigFileStateByLabel(String label) { Session session = HibernateFactory.getSession(); return (ConfigFileState) session.getNamedQuery("ConfigFileState.findByLabel").setString("label", label) //Retrieve from cache if there .setCacheable(true).uniqueResult(); } /** * Returns the the config file types associted to the given label * @param label the filte type label * @return config filetype object */ static ConfigFileType lookupConfigFileTypeByLabel(String label) { Session session = HibernateFactory.getSession(); return (ConfigFileType) session.getNamedQuery("ConfigFileType.findByLabel").setString("label", label) //Retrieve from cache if there .setCacheable(true).uniqueResult(); } /** * Return a <code>ConfigInfo</code> from the username, groupname, file mode, and * selinux context. If no corresponding entry exists yet in the database, one will be * created. * * Uses the stored procedure <code>lookup_config_info</code> to get the id of the * ConfigInfo and then uses hibernate to lookup using that id. * * Note: we should use the stored procedure because it is autonomous and avoids race * conditions. However, we also need to make sure that hibernate knows that the object * already exists in the database. Therefore after storing it, instead of simply * creating the object in java, we ask hibernate to look it up (which will find the * correct created and modified dates as well). * * ConfigInfo's have a unique constraint around username, groupname, and filemode * so we can't just create them willy nilly. * * @param username The linux username associated with a file * @param groupname The linux groupname associated with a file * @param filemode The three digit file mode (ex: 655) * @param selinuxCtx The SELinux context * @param symlinkTargetPath a target path for symlink or null for a non symlink path * @return The ConfigInfo found or inserted. */ public static ConfigInfo lookupOrInsertConfigInfo(String username, String groupname, Long filemode, String selinuxCtx, String symlinkTargetPath) { Long id = lookupConfigInfo(username, groupname, filemode, selinuxCtx, symlinkTargetPath); return lookupConfigInfoById(id); } /** * Using a stored procedure that looks up the config info and will * create one if it does not exist. * @param user The linux username associated with a file * @param group The linux groupname associated with a file * @param filemode The three digit file mode (ex: 655) * @param selinuxCtx The SELinux context * @return The id of the found config info */ private static Long lookupConfigInfo(String user, String group, Long filemode, String selinuxCtx, String symlinkTargetPath) { CallableMode m = ModeFactory.getCallableMode("config_queries", "lookup_config_info"); Map inParams = new HashMap(); Map outParams = new HashMap(); inParams.put("username_in", user); inParams.put("groupname_in", group); inParams.put("filemode_in", filemode); if ((selinuxCtx == null) || (selinuxCtx.isEmpty())) { inParams.put("selinuxCtx_in", null); } else { inParams.put("selinuxCtx_in", selinuxCtx); } if (!StringUtils.isBlank(symlinkTargetPath)) { ConfigFileName fn = lookupOrInsertConfigFileName(symlinkTargetPath); inParams.put("symlink_target_file_in", fn.getId()); } else { inParams.put("symlink_target_file_in", null); } outParams.put("info_id", new Integer(Types.NUMERIC)); Map out = m.execute(inParams, outParams); return (Long) out.get("info_id"); } /** * Return a <code>ConfigFileName</code> for the path given. If no corresponding * entry exists yet in the database, one will be created. * * Uses the stored procedure <code>lookup_config_filename</code> to get the id of the * ConfigFileName and then uses hibernate to lookup using that id. * * Note: we should use the stored procedure because it is autonomous and avoids race * conditions. However, we also need to make sure that hibernate knows that the object * already exists in the database. Therefore after storing it, instead of simply * creating the object in java, we ask hibernate to look it up (which will find the * correct created and modified dates as well). * * ConfigFileName's have a unique constraint around path so we can't just create * them willy nilly. * * @param path the path for the <code>ConfigFileName</code> * @return The <code>ConfigFileName</code> found */ public static ConfigFileName lookupOrInsertConfigFileName(String path) { Long id = lookupConfigFileName(path); return lookupConfigFileNameById(id); } /** * Using a stored procedure that looks up the config file name and will * create one if it does not exist. * @param path the path for the <code>ConfigFileName</code> * @return The id of the found config file name */ private static Long lookupConfigFileName(String path) { CallableMode m = ModeFactory.getCallableMode("config_queries", "lookup_config_filename"); Map inParams = new HashMap(); Map outParams = new HashMap(); inParams.put("name_in", path); outParams.put("name_id", new Integer(Types.NUMERIC)); Map out = m.execute(inParams, outParams); return (Long) out.get("name_id"); } /** * Remove a ConfigChannel. * This uses a stored procedure and the stored procedure is required * as it performs logic to determine the amount of org quota is now * used and appropriately updates those tables. * @param channel Channel to remove */ public static void removeConfigChannel(ConfigChannel channel) { CallableMode m = ModeFactory.getCallableMode("config_queries", "remove_config_channel"); Map inParams = new HashMap(); Map outParams = new HashMap(); inParams.put("config_channel_id_in", channel.getId()); m.execute(inParams, outParams); } /** * Remove a ConfigFile. * This uses a stored procedure and the stored procedure is required * as it performs logic to determine the amount of org quota is now * used and appropriately updates those tables. * @param file Config File to remove */ public static void removeConfigFile(ConfigFile file) { CallableMode m = ModeFactory.getCallableMode("config_queries", "remove_config_file"); Map inParams = new HashMap(); Map outParams = new HashMap(); inParams.put("config_file_id_in", file.getId()); m.execute(inParams, outParams); } /** * Remove a ConfigRevision. * This uses a stored procedure and the stored procedure is required * as it performs logic to determine the amount of org quota is now * used and appropriately updates those tables. * @param revision Revision to remove * @param orgId The id for the org in which this revision is located. * @return whether the parent file was deleted too. */ public static boolean removeConfigRevision(ConfigRevision revision, Long orgId) { boolean latest = false; ConfigFile file = revision.getConfigFile(); //is this revision the latest revision? if (file.getLatestConfigRevision().getId().equals(revision.getId())) { latest = true; } CallableMode m = ModeFactory.getCallableMode("config_queries", "remove_config_revision"); Map inParams = new HashMap(); Map outParams = new HashMap(); inParams.put("config_revision_id_in", revision.getId()); inParams.put("org_id", orgId); m.execute(inParams, outParams); if (latest) { //We just deleted the latest revision and now the config file has no idea //what its latest revision is, so we will find out. Map map = getMaxRevisionForFile(file); if (map != null) { Long id = (Long) map.get("id"); file.setLatestConfigRevision(lookupConfigRevisionById(id)); commit(file); } else { //there are no revisions in this file, delete the file. removeConfigFile(file); return true; } } return false; } /** * Create a local config channel for the given server. Fills in the necessary * fields with standard information. * @param server The server used to help populate required fields * @param type The type of the config channel. Either sandbox or local override. * @return The new local config channel. */ public static ConfigChannel createNewLocalChannel(Server server, ConfigChannelType type) { ConfigChannel retval = newConfigChannel(); retval.setOrg(server.getOrg()); retval.setConfigChannelType(type); retval.setCreated(new Date()); retval.setModified(new Date()); //The name of the channel should always be the server name for //local config channels. See bug #203406 retval.setName(server.getName()); retval.setLabel(server.getId().toString()); //This is an english string. However, users should never see a description of //a local config channel. For all purposes, this is a useless field that only //exists because we currently treat local config channels exactly the same as //global config channels. retval.setDescription("Auto-generated " + type.getLabel() + " config channel"); //TODO: put the following line back. It is not here now because Server.findLocal // does this task for us. It belongs here, but this would currently cause // an infinite loop based on how setSandboxOverride works. //server.setSandboxOverride(retval); commit(retval); return retval; } /** * Creates a new revision object from the give input stream. The size is set to * be the given size. The revision object is placed into the given config file * and uses the meta-data from the latest revision in that file. * @param usr The user that is making the new revision * @param stream The input stream containing the content for this revision. * @param size The size of the input stream to be read. * @param file The parent object for this config revision. * @return The newly created config revision. */ public static ConfigRevision createNewRevisionFromStream(User usr, InputStream stream, Long size, ConfigFile file) { //get a copy of the latest revision (to copy meta-data) ConfigRevision revision = file.getLatestConfigRevision().copy(); /* * We need to make five changes to the current revision. * 1. increment the revision number * 2. replace the content * 3. magic-decide whether this revision is binary * 4. compute the md5sum * 5. give it a new id */ //Step 1 //For database integrity, we won't just increment the latest revision number and //hope that nobody has futzed around. We will get the max revision and increment. Long next = getNextRevisionForFile(file); revision.setRevision(next); revision.setChangedById(usr.getId()); //Steps 2-4 if (revision.isFile()) { revision.setConfigContent(createNewContentFromStream(stream, size, revision.getConfigContent().isBinary(), revision.getConfigContent().getDelimStart(), revision.getConfigContent().getDelimEnd())); } //Step 5 revision.setId(null); revision = commit(revision); file.setLatestConfigRevision(revision); commit(file); return revision; } /** * Creates a ConfigContent object whose BLOB is filled with the bytes from the * specified stream * @param stream stream containing the content * @param size number of bytes to read * @param isBinary true if the content is to be treated as binary (which means we * won't expand macros, morph EOL, or let you edit it from the web UI) * @param delimStart start delimeter or null for binary * @param delimEnd end delimeter or null for binary * @return filled-in ConfigContent */ public static ConfigContent createNewContentFromStream(InputStream stream, Long size, boolean isBinary, String delimStart, String delimEnd) { ConfigContent content = ConfigurationFactory.newConfigContent(); content.setCreated(new Date()); content.setModified(new Date()); content.setFileSize(size); byte[] foo = bytesFromStream(stream, size); content.setContents(foo); Checksum newChecksum = ChecksumFactory.safeCreate(SHA256Crypt.sha256Hex(foo), "sha256"); content.setChecksum(newChecksum); content.setBinary(isBinary); content.setDelimStart(delimStart); content.setDelimEnd(delimEnd); return content; } /** * Convert input stream to byte array * @param stream input stream * @param size stream size * @return byte array */ public static byte[] bytesFromStream(InputStream stream, Long size) { byte[] foo = new byte[size.intValue()]; try { //this silly bit of logic is to ensure that we read as much from the file //as we possibly can. Most likely, stream.read(foo) would do the exact same //thing, but according to the javadoc, that may not always be the case. int offset = 0; int read = 0; // mark and reset stream, so that stream can be re-read later stream.mark(size.intValue()); do { read = stream.read(foo, offset, (foo.length - offset)); offset += read; } while (read > 0 && offset < foo.length); stream.reset(); } catch (IOException e) { log.error("IOException while reading config content from input stream!", e); throw new RuntimeException("IOException while reading config content from" + " input stream!"); } return foo; } private static Map getMaxRevisionForFile(ConfigFile file) { Map<String, Object> params = new HashMap<String, Object>(); params.put("cfid", file.getId()); SelectMode m = ModeFactory.getMode("config_queries", "max_revision_for_file"); DataResult dr = m.execute(params); if (dr.isEmpty()) { return null; //no revisions left. } return (Map) dr.get(0); } /** * Returns new revision number for config file * @param file config file * @return next revision number */ public static Long getNextRevisionForFile(ConfigFile file) { Map results = getMaxRevisionForFile(file); Long next = new Long(((Long) results.get("revision")).longValue() + 1); return next; } /** * Copies the given config revision to the given channel. * If there is a candidate config file that exists in that channel, this * revision gets set as the newest revision there. * Otherwise, a new config file is created with this as its only revision. * @param usr The user asking for the new revision * @param revision The revision to be copied * @param channel The channel to be copied into */ public static void copyRevisionToChannel(User usr, ConfigRevision revision, ConfigChannel channel) { /* * 1. Find any candidate config files already in the channel. * 2. Make a copy of the revision * 3. Associate the revision and the file * 4. save */ //Step 1. ConfigFileName name = revision.getConfigFile().getConfigFileName(); ConfigFile file = lookupConfigFileByChannelAndName(channel.getId(), name.getId()); Long rev = null; if (file == null) { //if a candidate does not exist, create one. rev = new Long(1); file = ConfigurationFactory.newConfigFile(); file.setConfigChannel(channel); file.setConfigFileName(name); file.setConfigFileState(ConfigFileState.normal()); file.setCreated(new Date()); file.setModified(new Date()); file = commit(file); } else { rev = getNextRevisionForFile(file); } //Step 2 ConfigRevision newRevision = revision.copy(); newRevision.setRevision(rev); newRevision.setChangedById(usr.getId()); newRevision.setId(null); //Step 3 newRevision.setConfigFile(file); //Step 4 newRevision = commit(newRevision); file.setLatestConfigRevision(newRevision); commit(file); } /** * Returns a localized string for the name of a config channel. This is here * because local and sandbox config channels are created automatically and therefore * have english names. This is located in this file to be a central location for * the logic. * @param type The type of the channel (one of the labels for * CONFIG_CHANNEL_TYPE_* constants) * @param channel The name of the channel, the system name for local channels. * @return A localized string for channel name */ public static String getChannelNameDisplay(String type, String channel) { if (type == null) { throw new IllegalArgumentException("Error: channel type cannot be null"); } if (ConfigChannelType.global().getLabel().equals(type)) { return channel; //for global channels, there name is the channel name. } else if (ConfigChannelType.local().getLabel().equals(type)) { return LocalizationService.getInstance().getMessage("config_channel_name.local", channel); } else if (ConfigChannelType.sandbox().getLabel().equals(type)) { return LocalizationService.getInstance().getMessage("config_channel_name.sandbox", channel); } else { throw new IllegalArgumentException( "Error getting channel name display." + " Invalid channel type given."); } } }