diff --git a/server/src/com/cloud/agent/manager/AgentManagerImpl.java b/server/src/com/cloud/agent/manager/AgentManagerImpl.java index a8c0c7fdfcd..59d38576b34 100755 --- a/server/src/com/cloud/agent/manager/AgentManagerImpl.java +++ b/server/src/com/cloud/agent/manager/AgentManagerImpl.java @@ -383,7 +383,7 @@ public class AgentManagerImpl implements AgentManager, HandlerFactory, Manager { } } - s_logger.warn("No handling of agent control command: " + cmd.toString() + " sent from " + attache.getId()); + s_logger.warn("No handling of agent control command: " + cmd + " sent from " + attache.getId()); return new AgentControlAnswer(cmd); } @@ -601,7 +601,7 @@ public class AgentManagerImpl implements AgentManager, HandlerFactory, Manager { Status currentState = host.getStatus(); Status nextState = currentState.getNextStatus(Status.Event.Remove); if (nextState == null) { - s_logger.debug("There is no transition from state " + currentState.toString() + " to state " + Status.Event.Remove.toString()); + s_logger.debug("There is no transition from state " + currentState + " to state " + Status.Event.Remove); return false; } @@ -981,7 +981,7 @@ public class AgentManagerImpl implements AgentManager, HandlerFactory, Manager { long hostId = attache.getId(); - s_logger.info("Host " + hostId + " is disconnecting with event " + event.toString()); + s_logger.info("Host " + hostId + " is disconnecting with event " + event); HostVO host = _hostDao.findById(hostId); if (host == null) { @@ -993,7 +993,7 @@ public class AgentManagerImpl implements AgentManager, HandlerFactory, Manager { final Status currentState = host.getStatus(); if (currentState == Status.Down || currentState == Status.Alert || currentState == Status.Removed) { if (s_logger.isDebugEnabled()) { - s_logger.debug("Host " + hostId + " is already " + currentState.toString()); + s_logger.debug("Host " + hostId + " is already " + currentState); } if (currentState != Status.PrepareForMaintenance) { removeAgent(attache, currentState); @@ -1006,21 +1006,21 @@ public class AgentManagerImpl implements AgentManager, HandlerFactory, Manager { return false; } - s_logger.debug("There is no transition from state " + currentState.toString() + " and event " + event.toString()); + s_logger.debug("There is no transition from state " + currentState + " and event " + event); assert false : "How did we get here. Look at the FSM"; return false; } if (s_logger.isDebugEnabled()) { - s_logger.debug("The next state is " + nextState.toString() + ", current state is " + currentState); + s_logger.debug("The next state is " + nextState + ", current state is " + currentState); } // Now we go and correctly diagnose what the actual situation is if (nextState == Status.Alert && investigate) { - s_logger.info("Investigating why host " + hostId + " has disconnected with event " + event.toString()); + s_logger.info("Investigating why host " + hostId + " has disconnected with event " + event); final Status determinedState = investigate(attache); - s_logger.info("The state determined is " + (determinedState != null ? determinedState.toString() : "undeterminable")); + s_logger.info("The state determined is " + determinedState); if (determinedState == null || determinedState == Status.Down) { s_logger.error("Host is down: " + host.getId() + "-" + host.getName() + ". Starting HA on the VMs"); @@ -1446,7 +1446,7 @@ public class AgentManagerImpl implements AgentManager, HandlerFactory, Manager { try { Host h = _hostDao.findById(hostId); if (h == null || h.getRemoved() != null) { - s_logger.debug("Host with id " + hostId.toString() + " doesn't exist"); + s_logger.debug("Host with id " + hostId + " doesn't exist"); return null; } Status status = h.getStatus(); @@ -1460,7 +1460,7 @@ public class AgentManagerImpl implements AgentManager, HandlerFactory, Manager { } if (s_logger.isDebugEnabled() && answer.getDetails() != null) { - s_logger.debug("Details from executing " + cmd.getClass().toString() + ": " + answer.getDetails()); + s_logger.debug("Details from executing " + cmd.getClass() + ": " + answer.getDetails()); } return answer; @@ -1530,7 +1530,7 @@ public class AgentManagerImpl implements AgentManager, HandlerFactory, Manager { List vms = _haMgr.findTakenMigrationWork(); for (VMInstanceVO vm : vms) { if (vm.getHostId() != null && vm.getHostId() == hostId) { - s_logger.info("Unable to cancel migration because the vm is being migrated: " + vm.toString()); + s_logger.info("Unable to cancel migration because the vm is being migrated: " + vm); return false; } } @@ -1595,8 +1595,8 @@ public class AgentManagerImpl implements AgentManager, HandlerFactory, Manager { } state = host.getStatus(); if (state == Status.Disconnected || state == Status.Updating) { - s_logger.debug("Unable to put host " + hostId + " in matinenance mode because it is currently in " + state.toString()); - throw new AgentUnavailableException("Agent is in " + state.toString() + " state. Please wait for it to become Alert state try again.", hostId); + s_logger.debug("Unable to put host " + hostId + " in matinenance mode because it is currently in " + state); + throw new AgentUnavailableException("Agent is in " + state + " state. Please wait for it to become Alert state try again.", hostId); } } while (!_hostDao.updateStatus(host, Event.MaintenanceRequested, _nodeId)); @@ -1865,12 +1865,12 @@ public class AgentManagerImpl implements AgentManager, HandlerFactory, Manager { server = _hostDao.persist(server); id = server.getId(); - s_logger.info("New " + server.getType().toString() + " host connected w/ guid " + startup.getGuid() + " and id is " + id); + s_logger.info("New " + server.getType() + " host connected w/ guid " + startup.getGuid() + " and id is " + id); } else { if (!_hostDao.connect(server, _nodeId)) { - throw new CloudRuntimeException("Agent cannot connect because the current state is " + server.getStatus().toString()); + throw new CloudRuntimeException("Agent cannot connect because the current state is " + server.getStatus()); } - s_logger.info("Old " + server.getType().toString() + " host reconnected w/ id =" + id); + s_logger.info("Old " + server.getType() + " host reconnected w/ id =" + id); } return server; @@ -2201,7 +2201,7 @@ public class AgentManagerImpl implements AgentManager, HandlerFactory, Manager { if (attache == null) { request.logD("Processing the first command "); if (!(cmd instanceof StartupCommand)) { - s_logger.warn("Throwing away a request because it came through as the first command on a connect: " + request.toString()); + s_logger.warn("Throwing away a request because it came through as the first command on a connect: " + request); return; } StartupCommand startup = (StartupCommand) cmd; @@ -2228,28 +2228,28 @@ public class AgentManagerImpl implements AgentManager, HandlerFactory, Manager { attache = handleConnect(link, startups); } catch (final IllegalArgumentException e) { _alertMgr.sendAlert(AlertManager.ALERT_TYPE_HOST, 0, new Long(0), "Agent from " + startup.getPrivateIpAddress() + " is unable to connect due to " + e.getMessage(), "Agent from " - + startup.getPrivateIpAddress() + " is unable to connect with " + request.toString() + " because of " + e.getMessage()); - s_logger.warn("Unable to create attache for agent: " + request.toString(), e); + + startup.getPrivateIpAddress() + " is unable to connect with " + request + " because of " + e.getMessage()); + s_logger.warn("Unable to create attache for agent: " + request, e); response = new Response(request, new StartupAnswer((StartupCommand) cmd, e.getMessage()), _nodeId, -1); } catch (ConnectionException e) { _alertMgr.sendAlert(AlertManager.ALERT_TYPE_HOST, 0, new Long(0), "Agent from " + startup.getPrivateIpAddress() + " is unable to connect due to " + e.getMessage(), "Agent from " - + startup.getPrivateIpAddress() + " is unable to connect with " + request.toString() + " because of " + e.getMessage()); - s_logger.warn("Unable to create attache for agent: " + request.toString(), e); + + startup.getPrivateIpAddress() + " is unable to connect with " + request + " because of " + e.getMessage()); + s_logger.warn("Unable to create attache for agent: " + request, e); response = new Response(request, new StartupAnswer((StartupCommand) cmd, e.getMessage()), _nodeId, -1); } catch (final CloudRuntimeException e) { _alertMgr.sendAlert(AlertManager.ALERT_TYPE_HOST, 0, new Long(0), "Agent from " + startup.getPrivateIpAddress() + " is unable to connect due to " + e.getMessage(), "Agent from " - + startup.getPrivateIpAddress() + " is unable to connect with " + request.toString() + " because of " + e.getMessage()); - s_logger.warn("Unable to create attache for agent: " + request.toString(), e); + + startup.getPrivateIpAddress() + " is unable to connect with " + request + " because of " + e.getMessage()); + s_logger.warn("Unable to create attache for agent: " + request, e); } if (attache == null) { if (response == null) { - s_logger.warn("Unable to create attache for agent: " + request.toString()); + s_logger.warn("Unable to create attache for agent: " + request); response = new Response(request, new StartupAnswer((StartupCommand) request.getCommand(), "Unable to register this agent"), _nodeId, -1); } try { link.send(response.toBytes(), true); } catch (final ClosedChannelException e) { - s_logger.warn("Response was not sent: " + response.toString()); + s_logger.warn("Response was not sent: " + response); } return; } @@ -2261,18 +2261,18 @@ public class AgentManagerImpl implements AgentManager, HandlerFactory, Manager { if (cmd instanceof PingRoutingCommand) { final PingRoutingCommand ping = (PingRoutingCommand) cmd; if (ping.getNewStates().size() > 0) { - s_logger.debug("SeqA " + hostId + "-" + request.getSequence() + ": Processing " + request.toString()); + s_logger.debug("SeqA " + hostId + "-" + request.getSequence() + ": Processing " + request); } else { logD = false; s_logger.debug("Ping from " + hostId); - s_logger.trace("SeqA " + hostId + "-" + request.getSequence() + ": Processing " + request.toString()); + s_logger.trace("SeqA " + hostId + "-" + request.getSequence() + ": Processing " + request); } } else if (cmd instanceof PingCommand) { logD = false; s_logger.debug("Ping from " + hostId); - s_logger.trace("SeqA " + attache.getId() + "-" + request.getSequence() + ": Processing " + request.toString()); + s_logger.trace("SeqA " + attache.getId() + "-" + request.getSequence() + ": Processing " + request); } else { - s_logger.debug("SeqA " + attache.getId() + "-" + request.getSequence() + ": Processing " + request.toString()); + s_logger.debug("SeqA " + attache.getId() + "-" + request.getSequence() + ": Processing " + request); } } @@ -2351,26 +2351,26 @@ public class AgentManagerImpl implements AgentManager, HandlerFactory, Manager { response = new Response(request, answers, _nodeId, attache.getId()); if (s_logger.isDebugEnabled()) { if (logD) { - s_logger.debug("SeqA " + attache.getId() + "-" + response.getSequence() + ": Sending " + response.toString()); + s_logger.debug("SeqA " + attache.getId() + "-" + response.getSequence() + ": Sending " + response); } else { - s_logger.trace("SeqA " + attache.getId() + "-" + response.getSequence() + ": Sending " + response.toString()); + s_logger.trace("SeqA " + attache.getId() + "-" + response.getSequence() + ": Sending " + response); } } try { link.send(response.toBytes()); } catch (final ClosedChannelException e) { - s_logger.warn("Unable to send response because connection is closed: " + response.toString()); + s_logger.warn("Unable to send response because connection is closed: " + response); } } protected void processResponse(final Link link, final Response response) { final AgentAttache attache = (AgentAttache) link.attachment(); if (attache == null) { - s_logger.warn("Unable to process: " + response.toString()); + s_logger.warn("Unable to process: " + response); } if (!attache.processAnswers(response.getSequence(), response)) { - s_logger.info("Host " + attache.getId() + " - Seq " + response.getSequence() + ": Response is not processed: " + response.toString()); + s_logger.info("Host " + attache.getId() + " - Seq " + response.getSequence() + ": Response is not processed: " + response); } } diff --git a/utils/src/com/cloud/utils/db/Condition.java b/utils/src/com/cloud/utils/db/Condition.java new file mode 100644 index 00000000000..5ef0be5d8f9 --- /dev/null +++ b/utils/src/com/cloud/utils/db/Condition.java @@ -0,0 +1,101 @@ +/** + * Copyright (C) 2010 Cloud.com, Inc. All rights reserved. + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ +package com.cloud.utils.db; + +import java.util.HashMap; + +import com.cloud.utils.db.SearchCriteria.Op; + +public class Condition { + Where _where; + Attribute _attr; + String _as; + SearchCriteria.Op _op; + String _paramName; + + protected Condition(Where where, Attribute attr, String as) { + assert (where != null) : "What am I going to return to the user when Where is null?"; + assert (attr != null) : "What's the point of giving me a null attribute?"; + _where = where; + _attr = attr; + _as = as; + } + + protected NextWhere set(Op op, String paramName) { + _op = op; + _paramName = paramName; + Where where = _where; + _where = null; + return where; + } + + public NextWhere eq(String paramName) { + return set(Op.EQ, paramName); + } + + public NextWhere lt(String paramName) { + return set(Op.LT, paramName); + } + + public NextWhere lteq(String paramName) { + return set(Op.LTEQ, paramName); + } + + public NextWhere gt(String paramName) { + return set(Op.GT, paramName); + } + + public NextWhere isNull() { + return set(Op.NULL, null); + } + + public NextWhere isNotNull() { + return set(Op.NNULL, null); + } + + public NextWhere in(String paramName) { + _op = Op.IN; + _paramName = paramName; + return _where; + } + + protected String getParamName() { + assert (_paramName instanceof String) : "Well, how can we get back a parameter name if it was not assigned one?"; + return _paramName; + } + + @Override + public boolean equals(Object obj) { + return _paramName.equals(obj); + } + + @Override + public int hashCode() { + return _paramName.hashCode(); + } + + public void toSql(StringBuilder builder, HashMap values) { + if (_as != null) { + builder.append(_as); + } else { + builder.append(_attr.table); + } + builder.append(".").append(_attr.columnName); + } + +} diff --git a/utils/src/com/cloud/utils/db/FirstWhere.java b/utils/src/com/cloud/utils/db/FirstWhere.java new file mode 100644 index 00000000000..23f2d771b02 --- /dev/null +++ b/utils/src/com/cloud/utils/db/FirstWhere.java @@ -0,0 +1,30 @@ +/** + * Copyright (C) 2010 Cloud.com, Inc. All rights reserved. + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ +package com.cloud.utils.db; + +public interface FirstWhere { + Condition field(Object field); + + Condition field(Object field, String as); + + NextWhere text(String text, String... paramNames); + + FirstWhere op(); + + void done(); +} diff --git a/utils/src/com/cloud/utils/db/GenericDaoBase.java b/utils/src/com/cloud/utils/db/GenericDaoBase.java index 43fe7599027..6302c6bd817 100755 --- a/utils/src/com/cloud/utils/db/GenericDaoBase.java +++ b/utils/src/com/cloud/utils/db/GenericDaoBase.java @@ -115,7 +115,7 @@ public abstract class GenericDaoBase implements Gene protected final static TimeZone s_gmtTimeZone = TimeZone.getTimeZone("GMT"); - protected final static Map, GenericDao> s_daoMaps = new ConcurrentHashMap, GenericDao>(71); + protected final static Map, GenericDaoBase> s_daoMaps = new ConcurrentHashMap, GenericDaoBase>(71); protected Class _entityBeanType; protected String _table; @@ -156,9 +156,9 @@ public abstract class GenericDaoBase implements Gene protected String _name; - public static GenericDao getDao(Class entityType) { + public static GenericDaoBase getDao(Class entityType) { @SuppressWarnings("unchecked") - GenericDao dao = (GenericDao)s_daoMaps.get(entityType); + GenericDaoBase dao = (GenericDaoBase)s_daoMaps.get(entityType); assert dao != null : "Unable to find DAO for " + entityType + ". Are you sure you waited for the DAO to be initialized before asking for it?"; return dao; } @@ -172,6 +172,10 @@ public abstract class GenericDaoBase implements Gene factory.setCallback(0, builder); return builder; } + + public final Map getAllAttributes() { + return _allAttributes; + } @SuppressWarnings("unchecked") protected GenericDaoBase() { diff --git a/utils/src/com/cloud/utils/db/JoinQueryBuilder.java b/utils/src/com/cloud/utils/db/JoinQueryBuilder.java new file mode 100644 index 00000000000..d0bc2924d78 --- /dev/null +++ b/utils/src/com/cloud/utils/db/JoinQueryBuilder.java @@ -0,0 +1,32 @@ +/** + * Copyright (C) 2010 Cloud.com, Inc. All rights reserved. + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ +package com.cloud.utils.db; + +/** + * JoinQueryBuilder builds queries for joins between multiple tables. + * + */ +public interface JoinQueryBuilder { + Select selectField(Object column); + + On innerJoin(Class entityClazz); + + J entity(Class entityClazz); + + FirstWhere where(); +} diff --git a/utils/src/com/cloud/utils/db/Merovingian2.java b/utils/src/com/cloud/utils/db/Merovingian2.java index 51c755ea34d..eeb1e99637c 100644 --- a/utils/src/com/cloud/utils/db/Merovingian2.java +++ b/utils/src/com/cloud/utils/db/Merovingian2.java @@ -66,6 +66,7 @@ public class Merovingian2 extends StandardMBean implements MerovingianMBean { Connection conn = null; try { conn = Transaction.getStandaloneConnectionWithException(); + conn.setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED); _concierge = new ConnectionConcierge("LockMaster", conn, true, true); } catch (SQLException e) { s_logger.error("Unable to get a new db connection", e); diff --git a/utils/src/com/cloud/utils/db/NextWhere.java b/utils/src/com/cloud/utils/db/NextWhere.java new file mode 100644 index 00000000000..403d6ea9d99 --- /dev/null +++ b/utils/src/com/cloud/utils/db/NextWhere.java @@ -0,0 +1,28 @@ +/** + * Copyright (C) 2010 Cloud.com, Inc. All rights reserved. + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ +package com.cloud.utils.db; + +public interface NextWhere extends FirstWhere { + NextWhere and(); + NextWhere or(); + NextWhere not(); + + @Override + void done(); + +} diff --git a/utils/src/com/cloud/utils/db/On.java b/utils/src/com/cloud/utils/db/On.java new file mode 100644 index 00000000000..090670b14b8 --- /dev/null +++ b/utils/src/com/cloud/utils/db/On.java @@ -0,0 +1,22 @@ +/** + * Copyright (C) 2010 Cloud.com, Inc. All rights reserved. + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ +package com.cloud.utils.db; + +public interface On { + +} diff --git a/utils/src/com/cloud/utils/db/QueryBuilder.java b/utils/src/com/cloud/utils/db/QueryBuilder.java new file mode 100644 index 00000000000..a7a15d7971a --- /dev/null +++ b/utils/src/com/cloud/utils/db/QueryBuilder.java @@ -0,0 +1,195 @@ +/** + * Copyright (C) 2010 Cloud.com, Inc. All rights reserved. + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ +package com.cloud.utils.db; + +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; + +import net.sf.cglib.proxy.Enhancer; +import net.sf.cglib.proxy.MethodInterceptor; +import net.sf.cglib.proxy.MethodProxy; + +import com.cloud.utils.Pair; + +public class QueryBuilder implements MethodInterceptor, SimpleQueryBuilder, SelectQueryBuilder, JoinQueryBuilder { + public enum Func { + NATIVE("@", 1), + MAX("MAX(@)", 1), + MIN("MIN(@)", 1), + FIRST("FIRST(@)", 1), + LAST("LAST(@)", 1), + SUM("SUM(@)", 1), + COUNT("COUNT(@)", 1), + DISTINCT("DISTINCT(@)", 1); + + private String func; + private int count; + + Func(String func, int params) { + this.func = func; + this.count = params; + } + + @Override + public String toString() { + return func; + } + + public int getCount() { + return count; + } + } + + protected HashMap, Pair, Object>> _entities; + protected ArrayList _specifiedAttrs = new ArrayList(); + protected T _resultSetClass; + protected ArrayList> _selects; + + public QueryBuilder(Class resultSetClass, Class... clazzes) { + _entities = new HashMap, Pair, Object>>(clazzes.length); + for (Class clazz : clazzes) { + GenericDaoBase dao = GenericDaoBase.getDao(clazz); + Enhancer searchEnhancer = new Enhancer(); + searchEnhancer.setSuperclass(clazz); + searchEnhancer.setCallback(this); + Object entity = searchEnhancer.create(); + + _entities.put(clazz, new Pair, Object>(dao, entity)); + } + } + + protected void clean() { + _specifiedAttrs = null; + _entities = null; + } + + /** + * Constructor for SelectQueryBuilder interface. Must specify the + * table to be performing the query on and the result class to place it in. + * @param entityClass entity class to do the query on. + * @param resultSetClass result class to put the result set in. + */ + public QueryBuilder(Class entityClass, Class resultSetClass) { + _entities = new HashMap, Pair, Object>>(1); + GenericDaoBase dao = GenericDaoBase.getDao(entityClass); + Enhancer searchEnhancer = new Enhancer(); + searchEnhancer.setSuperclass(entityClass); + searchEnhancer.setCallback(this); + Object entity = searchEnhancer.create(); + + _entities.put(entityClass, new Pair, Object>(dao, entity)); + } + + @Override + public SimpleQueryBuilder selectFields(Object... fields) { + assert _entities != null && _entities.size() == 1 : "Now you've done it....Stop casting interfaces on the QueryBuilder"; + assert _specifiedAttrs.size() > 0 : "You didn't specify any attributes"; + + if (_selects == null) { + _selects = new ArrayList>(fields.length); + } + + for (Attribute attr : _specifiedAttrs) { + _selects.add(new Select(this, null, attr)); + } + + _specifiedAttrs.clear(); + + return this; + } + + protected void set(GenericDaoBase dao , String name) { + Attribute attr = dao.getAllAttributes().get(name); + assert (attr != null) : "Searching for a field that's not there: " + name; + _specifiedAttrs.add(attr); + } + + + @Override + public Object intercept(Object entity, Method method, Object[] args, MethodProxy proxy) throws Throwable { + Class entityClass = entity.getClass().getSuperclass(); + + Pair, Object> daoInfo = _entities.get(entityClass); + assert (daoInfo != null) : "You need to specify " + entityClass + " as one of the entities in the Query"; + GenericDaoBase dao = daoInfo.first(); + + String name = method.getName(); + if (name.startsWith("get")) { + String fieldName = Character.toLowerCase(name.charAt(3)) + name.substring(4); + set(dao, fieldName); + return null; + } else if (name.startsWith("is")) { + String fieldName = Character.toLowerCase(name.charAt(2)) + name.substring(3); + set(dao, fieldName); + return null; + } else { + assert false : "Perhaps you need to make the method start with get or is?"; + } + return proxy.invokeSuper(entity, args); + } + + @Override + @SuppressWarnings("unchecked") + public E entity(Class clazz) { + return (E)_entities.get(clazz).second(); + } + + @Override + @SuppressWarnings("unchecked") + public S entity() { + return (S)_entities.values().iterator().next().second(); + } + + @Override + public FirstWhere where() { + return new Where(this); + } + + @Override + public SimpleQueryBuilder selectAll() { + return this; + } + + public List getSpecifiedAttributes() { + return _specifiedAttrs; + } + + public Attribute getSpecifiedAttribute() { + assert _specifiedAttrs.size() == 1 : "You can only specify one attribute"; + return _specifiedAttrs.get(0); + } + + @Override + public Select selectColumn(Object column) { + return null; + } + + @Override + public Select selectField(Object column) { + // TODO Auto-generated method stub + return null; + } + + @Override + public On innerJoin(Class entityClazz) { + // TODO Auto-generated method stub + return null; + } +} diff --git a/utils/src/com/cloud/utils/db/SearchCriteria.java b/utils/src/com/cloud/utils/db/SearchCriteria.java index d2756b2ebbf..00abb3a1606 100755 --- a/utils/src/com/cloud/utils/db/SearchCriteria.java +++ b/utils/src/com/cloud/utils/db/SearchCriteria.java @@ -123,8 +123,24 @@ public class SearchCriteria { private final List _groupByValues; private final Class _resultType; private final SelectType _selectType; + private final QueryBuilder _builder; + + protected SearchCriteria(QueryBuilder builder) { + _builder = builder; + _attrs = null; + _conditions = null; + _additionals = null; + _counter = 0; + _joins = null; + _selects = null; + _groupBy = null; + _groupByValues = null; + _resultType = null; + _selectType = null; + } protected SearchCriteria(GenericSearchBuilder sb) { + this._builder = null; this._attrs = sb._attrs; this._conditions = sb._conditions; this._additionals = new ArrayList(); @@ -322,4 +338,10 @@ public class SearchCriteria { } } } + + public Pair> toSql() { + StringBuilder sql = new StringBuilder(); + + return new Pair>(sql.toString(), null); + } } diff --git a/utils/src/com/cloud/utils/db/Select.java b/utils/src/com/cloud/utils/db/Select.java new file mode 100644 index 00000000000..90d0096cdbd --- /dev/null +++ b/utils/src/com/cloud/utils/db/Select.java @@ -0,0 +1,53 @@ +/** + * Copyright (C) 2010 Cloud.com, Inc. All rights reserved. + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ +package com.cloud.utils.db; + +import java.lang.reflect.Field; + +public class Select { + QueryBuilder _builder; + Class _clazz; + Attribute _attr; + String _as; + Field _field; + + protected Select(QueryBuilder builder, Class clazz, Attribute attr) { + _builder = builder; + _clazz = clazz; + _attr = attr; + } + + public QueryBuilder into(String fieldName) { + if (fieldName != null) { + try { + _field = _clazz.getDeclaredField(fieldName); + _field.setAccessible(true); + } catch (SecurityException e) { + throw new RuntimeException("Unable to find " + fieldName + " in " + _clazz.getName(), e); + } catch (NoSuchFieldException e) { + throw new RuntimeException("Unable to find " + fieldName + " in " + _clazz.getName(), e); + } + } + return _builder; + } + + public QueryBuilder as(String as) { + _as = as; + return _builder; + } +} diff --git a/utils/src/com/cloud/utils/db/SelectQueryBuilder.java b/utils/src/com/cloud/utils/db/SelectQueryBuilder.java new file mode 100644 index 00000000000..69fe58e372c --- /dev/null +++ b/utils/src/com/cloud/utils/db/SelectQueryBuilder.java @@ -0,0 +1,30 @@ +/** + * Copyright (C) 2010 Cloud.com, Inc. All rights reserved. + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ +package com.cloud.utils.db; + +/** + * SelectQueryBuilder allows you to select a column into a class that + * is defined. + */ +public interface SelectQueryBuilder { + Select selectColumn(Object column); + + T entity(); + + FirstWhere where(); +} diff --git a/utils/src/com/cloud/utils/db/SimpleQueryBuilder.java b/utils/src/com/cloud/utils/db/SimpleQueryBuilder.java new file mode 100644 index 00000000000..f105c307cb4 --- /dev/null +++ b/utils/src/com/cloud/utils/db/SimpleQueryBuilder.java @@ -0,0 +1,49 @@ +/** + * Copyright (C) 2010 Cloud.com, Inc. All rights reserved. + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ +package com.cloud.utils.db; + +/** + * SimpleQueryBuilder builds queries against a single table. The + * result is stored into the entity that represents the table. + * + */ +public interface SimpleQueryBuilder { + /** + * Select all of the columns in the entity object. This is default so + * it's not necessary to make this method call at all. + */ + SimpleQueryBuilder selectAll(); + + /** + * Select the following columns + * @param columns array of columsn to select. + */ + SimpleQueryBuilder selectFields(Object... columns); + + /** + * @return the entity object we're building this query for. By using this + * entity object, you can specify which column to select or form + */ + S entity(); + + /** + * Starts the query conditionals. + * @return + */ + FirstWhere where(); +} diff --git a/utils/src/com/cloud/utils/db/Where.java b/utils/src/com/cloud/utils/db/Where.java new file mode 100644 index 00000000000..5da60e978b1 --- /dev/null +++ b/utils/src/com/cloud/utils/db/Where.java @@ -0,0 +1,82 @@ +/** + * Copyright (C) 2010 Cloud.com, Inc. All rights reserved. + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ +package com.cloud.utils.db; + +import java.util.ArrayList; +import java.util.List; + +/** + * Where implements any list of search conditions. + * + */ +public class Where implements FirstWhere, NextWhere { + QueryBuilder _builder; + List _conditions = new ArrayList(); + + protected Where(QueryBuilder builder) { + _builder = builder; + } + + @Override + public Condition field(Object useless, String as) { + Attribute attr = _builder.getSpecifiedAttribute(); + Condition cond = new Condition(this, attr, as); + _conditions.add(cond); + return cond; + } + + @Override + public Where and() { + _conditions.add(" ("); + return this; + } + + @Override + public Where or() { + _conditions.add(" OR "); + return this; + } + + @Override + public NextWhere not() { + _conditions.add(" NOT "); + return this; + } + + @Override + public NextWhere text(String text, String... paramNames) { + assert ((paramNames.length == 0 && !text.contains("?")) || (text.matches("\\?.*{" + paramNames.length + "}"))); + // TODO Auto-generated method stub + return null; + } + + @Override + public Condition field(Object useless) { + return field(useless, null); + } + + @Override + public FirstWhere op() { + _conditions.add("("); + return this; + } + + @Override + public void done() { + } +} diff --git a/utils/test/com/cloud/utils/db/QueryBuilderTest.java b/utils/test/com/cloud/utils/db/QueryBuilderTest.java new file mode 100644 index 00000000000..b6c0a41e05d --- /dev/null +++ b/utils/test/com/cloud/utils/db/QueryBuilderTest.java @@ -0,0 +1,126 @@ +package com.cloud.utils.db; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.SQLException; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.Table; + +import junit.framework.TestCase; + +import com.cloud.utils.component.ComponentLocator; +import com.cloud.utils.exception.CloudRuntimeException; + + +public class QueryBuilderTest extends TestCase { + @Entity + @Table(name="test") + public static class TestVO { + @Id + @GeneratedValue(strategy=GenerationType.IDENTITY) + long id; + + @Column(name="int") + int fieldInt; + + @Column(name="long") + Long fieldLong; + + @Column(name="string") + String fieldString; + + public String getFieldString() { + return fieldString; + } + + public int getFieldInt() { + return fieldInt; + } + + public long getFieldLong() { + return fieldLong; + } + + public TestVO() { + } + } + + public static class TestDao extends GenericDaoBase implements GenericDao { + protected TestDao() { + } + } + + public void setup() { + Connection conn = null; + PreparedStatement pstmt = null; + try { + conn = Transaction.getStandaloneConnection(); + + pstmt = conn.prepareStatement("CREATE TABLE `cloud`.`test` (" + + "`id` bigint unsigned NOT NULL UNIQUE AUTO_INCREMENT," + + "`int` int unsigned," + + "`long` bigint unsigned," + + "`string` varchar(255)," + + "PRIMARY KEY (`id`)" + + ") ENGINE=InnoDB DEFAULT CHARSET=utf8;"); + + pstmt.execute(); + + } catch (SQLException e) { + throw new CloudRuntimeException("Problem with sql", e); + } finally { + if (pstmt != null) { + try { + pstmt.close(); + } catch (SQLException e) { + } + } + if (conn != null) { + try { + conn.close(); + } catch (SQLException e) { + } + } + } + } + + public void teardown() { + Connection conn = null; + PreparedStatement pstmt = null; + try { + conn = Transaction.getStandaloneConnection(); + + pstmt = conn.prepareStatement("DROP TABLE IF EXISTS `cloud`.`test`"); + pstmt.execute(); + + } catch (SQLException e) { + throw new CloudRuntimeException("Problem with sql", e); + } finally { + if (pstmt != null) { + try { + pstmt.close(); + } catch (SQLException e) { + } + } + if (conn != null) { + try { + conn.close(); + } catch (SQLException e) { + } + } + } + } + + public void testSimpleQueryBuilder() { + TestDao dao = ComponentLocator.inject(TestDao.class); + SimpleQueryBuilder qb = new QueryBuilder(TestVO.class, TestVO.class); + qb.selectFields(qb.entity().getFieldLong()).where().field(qb.entity().getFieldInt()).eq("abc"); + + } + +}