Java tutorial
/** * Date Modified: $Date: 2010-06-30 15:19:58 +1000 (Wed, 30 Jun 2010) $ * Version: $Revision: 447 $ * * Copyright 2008 The Australian National University (ANU) * * 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 au.edu.apsr.pids.to; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.List; import net.handle.hdllib.AbstractMessage; import net.handle.hdllib.AbstractResponse; import net.handle.hdllib.AddValueRequest; import net.handle.hdllib.AdminRecord; import net.handle.hdllib.Common; import net.handle.hdllib.CreateHandleRequest; import net.handle.hdllib.RemoveValueRequest; import net.handle.hdllib.Encoder; import net.handle.hdllib.HandleException; import net.handle.hdllib.HandleResolver; import net.handle.hdllib.HandleValue; import net.handle.hdllib.ModifyValueRequest; import net.handle.hdllib.PublicKeyAuthenticationInfo; import net.handle.hdllib.ResolutionRequest; import net.handle.hdllib.ResolutionResponse; import net.handle.hdllib.Resolver; import net.handle.hdllib.Util; import au.edu.apsr.pids.dao.DAOException; import au.edu.apsr.pids.dao.HandleDAO; import au.edu.apsr.pids.util.HandleConfig; import au.edu.apsr.pids.util.Constants; import au.edu.apsr.pids.util.HandleSupport; import org.apache.log4j.Logger; import com.google.gson.*; import java.io.IOException; /** * Class representing a Handle object * * @author Scott Yeadon, ANU */ public class Handle { private static final Logger log = Logger.getLogger(Handle.class); private String handle; private HandleConfig handleConfig = null; private Resolver resolver = new Resolver(); private HashMap<String, String> hm = new HashMap<String, String>(); /** * Private constructor of a Handle object. Handles are created via other * method calls rather than constructed directly. */ private Handle() { this.handleConfig = HandleConfig.getHandleConfig(); // support workaround for HANDLE_NOT_FOUND_EXCEPTION hm.put(Constants.STD_TYPE_URL_STRING, Constants.STD_TYPE_URL_STRING); hm.put(Constants.XT_TYPE_DESC_STRING, Constants.XT_TYPE_DESC_STRING); } /** * Create a Handle object object using the provided Identifier and * handle values * * @return Handle * the Handle object * @param identifier * the agent Identifier object * @param hv * an array of HandleValue objects to be added to the handle * @throws DAOException * @throws HandleException * @throws IOException */ public static Handle create(Identifier identifier, HandleValue[] hv) throws DAOException, HandleException, IOException { Handle handleObject = new Handle(); HandleConfig hc = HandleConfig.getHandleConfig(); handleObject.setHandle(hc.getPrefix() + '/' + Handle.getNextSuffix()); AdminRecord admin = handleObject.createAdminRecord(Constants.NA_HANDLE_PREFIX + hc.getPrefix(), Constants.ADMIN_GROUP_IDX); HandleValue values[] = new HandleValue[hv.length + 2]; // load the passed values into the value array int i; for (i = 0; i < hv.length; i++) { values[i] = hv[i]; } // add the admin values values[i] = new HandleValue(); values[i].setIndex(Constants.ADMIN_IDX); values[i].setType(Common.STD_TYPE_HSADMIN); values[i].setData(Encoder.encodeAdminRecord(admin)); values[i].setTTL(Constants.DEFAULT_TTL); values[i + 1] = new HandleValue(); values[i + 1].setIndex(Constants.AGENT_IDX); values[i + 1].setType(Constants.XT_AGENTID); values[i + 1].setData(Util.encodeString(identifier.getHandle())); values[i + 1].setTTL(Constants.DEFAULT_TTL); AbstractResponse response = handleObject.createHandle(values); if (response.responseCode == AbstractMessage.RC_SUCCESS) { JsonObject obj = new JsonObject(); obj.addProperty("status", "success"); obj.addProperty("handle", handleObject.getHandle()); obj.addProperty("identifier", identifier.getIdentifier()); obj.addProperty("authDomain", identifier.getAuthDomain()); obj.addProperty("appId", identifier.getAppid()); String jsonStr = new Gson().toJson(obj); log.info(jsonStr); return handleObject; } else { log.info("Failed to create handle: " + response); return null; } } /** * Create an agent admin Handle object object using the provided identifier * string and authentication domain string * * @return Handle * the Handle object * @param identifier * the agent identifier string * @param authDomain * the agent authentication domain string * @throws DAOException * @throws HandleException */ public static Handle createAdmin(String identifier, String authDomain, String appId) throws DAOException, HandleException { Handle handleObject = new Handle(); HandleConfig hc = HandleConfig.getHandleConfig(); handleObject.setHandle(hc.getPrefix() + '/' + Handle.getNextSuffix()); AdminRecord admin = handleObject.createAdminRecord(Constants.NA_HANDLE_PREFIX + hc.getPrefix(), Constants.ADMIN_GROUP_IDX); HandleValue values[] = new HandleValue[4]; values[0] = new HandleValue(); values[0].setIndex(Constants.AGENT_DESC_IDX); values[0].setType(Constants.XT_TYPE_DESC); values[0].setAnyoneCanRead(false); values[0].setData(Util.encodeString(identifier + Identifier.separator + authDomain)); values[0].setTTL(Constants.DEFAULT_TTL); values[1] = new HandleValue(); values[1].setIndex(Constants.ADMIN_IDX); values[1].setType(Common.STD_TYPE_HSADMIN); values[1].setData(Encoder.encodeAdminRecord(admin)); values[1].setTTL(Constants.DEFAULT_TTL); values[2] = new HandleValue(); values[2].setIndex(Constants.AGENT_IDX); values[2].setType(Constants.XT_AGENTID); values[2].setData(Util.encodeString(handleObject.getHandle())); values[2].setTTL(Constants.DEFAULT_TTL); values[3] = new HandleValue(); values[3].setIndex(Constants.AGENT_DESC_APPIDX); values[3].setType(Constants.XT_APPID); values[3].setAnyoneCanRead(false); values[3].setData(Util.encodeString(appId)); values[3].setTTL(Constants.DEFAULT_TTL); AbstractResponse response = handleObject.createHandle(values); if (response.responseCode == AbstractMessage.RC_SUCCESS) { JsonObject obj = new JsonObject(); obj.addProperty("status", "success"); obj.addProperty("handle", handleObject.getHandle()); obj.addProperty("identifier", identifier); obj.addProperty("authDomain", authDomain); obj.addProperty("appId", appId); String jsonStr = new Gson().toJson(obj); log.info(jsonStr); log.info("Successfully created admin handle: " + handleObject.getHandle()); return handleObject; } else { log.info("Failed to create admin handle: " + response); return null; } } /** * Create the NA admin record for a new handle. The NA admin is provided * all permissions bar ADD_NA and DELETE_NA * * @return AdminRecord * an AdminRecord object representing the NA handle admin * @param handle * the NA admin handle in byte form * @param idx * the handle index the of the NA handle's HS_VLIST entry */ public AdminRecord createAdminRecord(String handle, int idx) { return new AdminRecord(Util.encodeString(handle), idx, AdminRecord.PRM_ADD_HANDLE, AdminRecord.PRM_DELETE_HANDLE, AdminRecord.PRM_NO_ADD_NA, AdminRecord.PRM_NO_DELETE_NA, AdminRecord.PRM_READ_VALUE, AdminRecord.PRM_MODIFY_VALUE, AdminRecord.PRM_REMOVE_VALUE, AdminRecord.PRM_ADD_VALUE, AdminRecord.PRM_MODIFY_ADMIN, AdminRecord.PRM_REMOVE_ADMIN, AdminRecord.PRM_ADD_ADMIN, AdminRecord.PRM_LIST_HANDLES); } /** * Add one or more values to the handle * * @return boolean * <code>true</code> if the value was added else <code>false</code> * @param value * an array of HandleValue objects * @throws HandleException */ public boolean addValue(HandleValue[] value) throws DAOException, HandleException { if (value.length < 1) { log.error("Empty value, unable to add value"); return false; } for (int i = 0; i < value.length; i++) { if (value[i].getTypeAsString().equals("URL")) { if (!HandleSupport.isValidURL(value[i].getDataAsString())) { log.error("Invalid value for URL type: " + value[i].getDataAsString()); return false; } } } byte idHandle[] = Util.encodeString(Constants.NA_HANDLE_PREFIX + handleConfig.getPrefix()); PublicKeyAuthenticationInfo pubKeyAuthInfo = new PublicKeyAuthenticationInfo(idHandle, Constants.SEC_KEY_IDX, handleConfig.getPrivateKey()); // if index has not been set, set it if (value[0].getIndex() == -1) { // get the next available index, not overly scalable but unlikely to hit // issues unless huge number of indexes for single handle int nextIndex = 0; Integer[] indexes = getSortedIndexes(); boolean foundIndex = false; while (!foundIndex) { nextIndex++; while (HandleSupport.isIndexReserved(nextIndex)) { nextIndex++; } int i; for (i = 0; i < indexes.length; i++) { if (nextIndex == indexes[i].intValue()) { break; } } if (i == indexes.length) { foundIndex = true; } } value[0].setIndex(nextIndex); } // do the add request AddValueRequest req = new AddValueRequest(Util.encodeString(this.getHandle()), value, pubKeyAuthInfo); AbstractResponse response = resolver.getResolver().processRequest(req); if (response.responseCode == AbstractMessage.RC_SUCCESS) { return true; } else { log.error("Error adding handle value to handle " + getHandle() + ": " + AbstractMessage.getResponseCodeMessage(response.responseCode)); return false; } } /** * Delete a value from the handle * * @return boolean * <code>true</code> if the value was deleted else <code>false</code> * @param index * the index of the value to delete * @throws HandleException */ public boolean deleteValue(int index) throws HandleException { byte idHandle[] = Util.encodeString(Constants.NA_HANDLE_PREFIX + handleConfig.getPrefix()); PublicKeyAuthenticationInfo pubKeyAuthInfo = new PublicKeyAuthenticationInfo(idHandle, Constants.SEC_KEY_IDX, handleConfig.getPrivateKey()); RemoveValueRequest req = new RemoveValueRequest(Util.encodeString(this.getHandle()), index, pubKeyAuthInfo); AbstractResponse response = resolver.getResolver().processRequest(req); if (response.responseCode == AbstractMessage.RC_SUCCESS) { return true; } else { log.error("Error deleting handle value from handle " + getHandle() + ": " + AbstractMessage.getResponseCodeMessage(response.responseCode)); return false; } } /** * Modify a handle value * * @return boolean * <code>true</code> if the value was deleted else <code>false</code> * @param index * the index of the value to modify * @param value * a string value to replace the existing value * @throws HandleException */ public boolean modifyValue(int index, String value) throws HandleException { boolean modified = false; HandleValue[] hv = getValues(index); if (hv.length == 0) { return modified; } if (hv[0].getTypeAsString().equals("URL")) { if (!HandleSupport.isValidURL(value)) { log.error("Invalid value for URL type: " + value); return modified; } } hv[0].setData(Util.encodeString(value)); hv[0].setTTL(Constants.DEFAULT_TTL); byte idHandle[] = Util.encodeString(Constants.NA_HANDLE_PREFIX + handleConfig.getPrefix()); PublicKeyAuthenticationInfo pubKeyAuthInfo = new PublicKeyAuthenticationInfo(idHandle, Constants.SEC_KEY_IDX, handleConfig.getPrivateKey()); ModifyValueRequest req = new ModifyValueRequest(Util.encodeString(this.getHandle()), hv, pubKeyAuthInfo); AbstractResponse response = resolver.getResolver().processRequest(req); if (response.responseCode == AbstractMessage.RC_SUCCESS) { modified = true; } else { log.error("Error modifying handle value for " + getHandle() + ": " + AbstractMessage.getResponseCodeMessage(response.responseCode)); modified = false; } return modified; } /** * Obtain the values of the handle * * @return HandleValue[] * An array of HandleValue objects * @throws HandleException */ public HandleValue[] getValues() throws HandleException { return resolver.resolveHandle(this.getHandle()); } /** * Obtain the handle value at the provided index * * @return HandleValue[] * An array comprising a single HandleValue object * @param index * The index of the value to return * @throws HandleException */ public HandleValue[] getValues(int index) throws HandleException { int[] indexes = { index }; return getValues(indexes); } /** * Obtain the handle values at the provided indexes * * @return HandleValue[] * An array of HandleValue objects * @param indexes * An array of indexes whose values are to be returned * @throws HandleException */ public HandleValue[] getValues(int[] indexes) throws HandleException { return resolver.resolveHandle(this.getHandle(), null, indexes, false); } /** * Obtain the handle values of the provided types * * @return HandleValue[] * An array of HandleValue objects * @param types * An array of types whose values are to be returned * @throws HandleException */ public HandleValue[] getValues(String[] types) throws HandleException { //return resolver.resolveHandle(this.getHandle(), types, null, false); HandleValue[] hvs = resolver.resolveHandle(this.getHandle()); return resolveAllowedValues(hvs); } /** * Set the handle string * * @param handle * A handle string */ public void setHandle(String handle) { this.handle = handle; } /** * Get the handle string of this Handle object * * @return String * the handle string */ public String getHandle() { return this.handle; } /** * Get the next handle suffix value * * @return long * the next available suffix value * @throws DAOException */ // TODO: Support other suffix formats by creating a Suffix interface // Current implementation will be long but should be able to add later // on different suffix types without major code changes. public static long getNextSuffix() throws DAOException { HandleDAO hdao = new HandleDAO(); return hdao.getNextSuffix(); } /** * Get a list of handles matching a data value * * @param data * A string contained within hamdles handles are to be returned * @param type * The handle value type (or null if all types) * @param pubReadOnly * Only include publicly readable values * @return List<Handle> * A list of handles with data matching the string. If type * is provided only matches within that type will be returned * @throws DAOException */ public static List<Handle> getHandlesByData(String data, String type, boolean pubReadOnly) throws HandleException, DAOException { HandleDAO hdao = new HandleDAO(); List<String> l = hdao.getHandlesByData(data, type, pubReadOnly); List<Handle> handleObjects = new ArrayList<Handle>(); try { for (Iterator<String> i = l.iterator(); i.hasNext();) { handleObjects.add(Handle.find(i.next())); } } catch (HandleException he) { log.info("Handle exception caught", he); } return handleObjects; } /** * Obtain the Handle object associated with the provided handle string * * @return Handle * The Handle object if found else <code>null</code> * @param handleString * A handle string * @throws HandleException */ public static Handle find(String handleString) throws HandleException { Handle handle = null; try { Resolver resolver = new Resolver(); HandleValue[] hv = resolver.resolveHandle(handleString); if (hv.length > 0) { handle = new Handle(); handle.setHandle(handleString); } } catch (HandleException he) { log.info("Handle exception caught", he); } return handle; } /** * Obtain a List of Handle objects belonging to the provided Identifier * * * @return List<Handle> * A list of Handle Objects * @param identifier * The Identifier object representing the agent whose handles * are to be returned * @throws HandleException * @throws DAOException */ public static List<String> getHandleStrings(Identifier identifier, String startHandle) throws HandleException, DAOException { HandleDAO hdao = new HandleDAO(); return hdao.getHandles(identifier, startHandle); } /** * Obtain a List of Handle objects belonging to the provided Identifier * * * @return List<Handle> * A list of Handle Objects * @param identifier * The Identifier object representing the agent whose handles * are to be returned * @param token * A resumption token for the listing * @throws HandleException * @throws DAOException */ /* public static List<Handle> getHandles(Identifier identifier, String token) throws HandleException, DAOException { Handle handle = null; HandleDAO hdao = new HandleDAO(); List<String> handles = hdao.getHandles(identifier, token); List<Handle> handleObjects = new ArrayList<Handle>(); try { for (Iterator<String> i = handles.iterator(); i.hasNext();) { handleObjects.add(Handle.find(i.next())); } } catch (HandleException he) { log.info("Handle exception caught", he); } return handleObjects; }*/ /** * Create a new handle record on the handle server * * @return AbstractResponse * the handle server response to the create request * @param hv * An array of HandleValue objects * @throws HandleException */ private AbstractResponse createHandle(HandleValue[] hv) throws HandleException { byte idHandle[] = Util.encodeString(Constants.NA_HANDLE_PREFIX + handleConfig.getPrefix()); PublicKeyAuthenticationInfo pubKeyAuthInfo = new PublicKeyAuthenticationInfo(idHandle, Constants.SEC_KEY_IDX, handleConfig.getPrivateKey()); CreateHandleRequest req = new CreateHandleRequest(Util.encodeString(this.getHandle()), hv, pubKeyAuthInfo); HandleResolver resolver = new HandleResolver(); resolver.traceMessages = true; return resolver.processRequest(req); } /** * Indicate whether an agent is the handle admin for this handle * * @return boolean * <code>true</code> if the provided Identifier is the * administrator of this handle else <code>false</code> * @param identifier * An Identifier object representing an agent * @throws HandleException */ public boolean isAdmin(Identifier identifier) throws HandleException { boolean isAdmin = false; // Get the record owner. May have to change this to types or reserved // indexes if have multiple admin agents int idx[] = { Constants.AGENT_IDX }; HandleValue[] agentHandle = resolver.resolveHandle(getHandle(), null, idx, false); if (agentHandle.length > 0) { byte idHandle[] = Util.encodeString(Constants.NA_HANDLE_PREFIX + handleConfig.getPrefix()); PublicKeyAuthenticationInfo pubKeyAuthInfo = new PublicKeyAuthenticationInfo(idHandle, Constants.SEC_KEY_IDX, handleConfig.getPrivateKey()); int idxDesc[] = { Constants.AGENT_DESC_IDX }; ResolutionRequest rReq = new ResolutionRequest(agentHandle[0].getData(), null, idxDesc, pubKeyAuthInfo); rReq.ignoreRestrictedValues = false; AbstractResponse response = resolver.getResolver().processRequest(rReq); if (response instanceof ResolutionResponse) { HandleValue[] ahv = ((ResolutionResponse) response).getHandleValues(); log.info("length=" + ahv.length); if (ahv.length > 0) { if (ahv[0].getDataAsString().equals(identifier.getAdminKey())) { isAdmin = true; } } } else { log.info("Unexpected response; type=" + response.getClass().getName() + ", msg=" + response.toString()); } } return isAdmin; } /** * Indicate whether a handle value can be deleted. A handle value * must be one of the allowed types in order for it to be deleted * * @return boolean * <code>true</code> if the value can be deleted * else <code>false</code> * @param index * the index of the value * @throws HandleException */ public boolean deleteAllowed(int index) throws HandleException { String[] types = { Constants.STD_TYPE_URL_STRING, Constants.XT_TYPE_DESC_STRING }; HandleValue[] hv = resolver.resolveHandle(getHandle(), types); boolean allowed = false; for (int i = 0; i < hv.length; i++) { if (hv[i].getIndex() == index) { allowed = true; break; } } return allowed; } /** * Indicate whether a handle value can be modified. A handle value * must be one of the allowed types in order for it to be modified * * @return boolean * <code>true</code> if the value can be modified * else <code>false</code> * @param index * the index of the value * @throws HandleException */ public boolean modifyAllowed(int index) throws HandleException { // currently same rules apply for modify as for delete return deleteAllowed(index); } /** * Obtain the index the provided value is located at * * @return int * the index the provided value is located at * @param value * the value whose index is to be returned * @throws HandleException */ public int getValueIndex(String value) throws HandleException { int index = -1; // String[] types = {Constants.STD_TYPE_URL_STRING, Constants.XT_TYPE_DESC_STRING}; // HandleValue[] hv = resolver.resolveHandle(getHandle(), types); HandleValue[] hv = resolveAllowedValues(resolver.resolveHandle(getHandle())); for (int i = 0; i < hv.length; i++) { if (hv[i].getDataAsString().equals(value)) { index = hv[i].getIndex(); break; } } return index; } // due to bug/ambiguity in resolveHandle with types, need to do this private HandleValue[] resolveAllowedValues(HandleValue[] hvs) { ArrayList<HandleValue> al = new ArrayList<HandleValue>(); for (int i = 0; i < hvs.length; i++) { if (hm.get(hvs[i].getTypeAsString()) != null) { al.add(hvs[i]); } } return al.toArray(new HandleValue[al.size()]); } /** * Determine whether an index is in use * * @return boolean * <code>true</code> if the index is available otherwise * <code>false</code> * @param index * the index to check * @throws HandleException */ public boolean isEmptyIndex(int index) throws HandleException { HandleValue[] hvs = resolver.resolveHandle(this.getHandle()); for (int i = 0; i < hvs.length; i++) { if (hvs[i].getIndex() == index) { return false; } } return true; } /** * Obtain an array of sorted indexes for a handle * * @return Array<Integer> * An array of sorted integers * @throws DAOException */ public Integer[] getSortedIndexes() throws DAOException { HandleDAO hdao = new HandleDAO(); return hdao.getSortedIndexes(this); } }