001 /*
002 * Databinder: a simple bridge from Wicket to Hibernate
003 * Copyright (C) 2006 Nathan Hamblen nathan@technically.us
004
005 * This library is free software; you can redistribute it and/or
006 * modify it under the terms of the GNU Lesser General Public
007 * License as published by the Free Software Foundation; either
008 * version 2.1 of the License, or (at your option) any later version.
009 *
010 * This library is distributed in the hope that it will be useful,
011 * but WITHOUT ANY WARRANTY; without even the implied warranty of
012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
013 * Lesser General Public License for more details.
014 *
015 * You should have received a copy of the GNU Lesser General Public
016 * License along with this library; if not, write to the Free Software
017 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
018 */
019
020 package net.databinder.components.hib;
021
022 import java.io.Serializable;
023
024 import net.databinder.models.hib.HibernateObjectModel;
025
026 import org.apache.wicket.Component;
027 import org.apache.wicket.WicketRuntimeException;
028 import org.apache.wicket.markup.html.link.Link;
029 import org.apache.wicket.model.CompoundPropertyModel;
030 import org.apache.wicket.model.IModel;
031 import org.hibernate.Session;
032
033 /**
034 * Form for a persistent model object nested in a BoundCompoundPropertyModel.
035 * Saves the model object to persistent storage when a valid form is submitted. This
036 * form can be a child component of any Wicket page.
037 * @author Nathan Hamblen
038 */
039 public class DataForm extends DataFormBase {
040 private Serializable version;
041
042 /**
043 * Instantiates this form and a new, blank instance of the given class as a persistent model
044 * object. By default the model object created is serialized and retained between requests until
045 * it is persisted.
046 * @param id
047 * @param modelClass for the persistent object
048 * @see HibernateObjectModel#setRetainUnsaved(boolean)
049 */
050 public DataForm(String id, Class modelClass) {
051 super(id, new CompoundPropertyModel(new HibernateObjectModel(modelClass)));
052 }
053
054 public DataForm(String id, HibernateObjectModel model) {
055 super(id, new CompoundPropertyModel(model));
056 setFactoryKey(model.getFactoryKey());
057 }
058
059 /**
060 * Instantiates this form with a persistent object of the given class and id.
061 * @param id Wicket id
062 * @param modelClass for the persistent object
063 * @param persistentObjectId id of the persistent object
064 */
065 public DataForm(String id, Class modelClass, Serializable persistentObjectId) {
066 super(id, new CompoundPropertyModel(new HibernateObjectModel(modelClass, persistentObjectId)));
067 }
068
069 /**
070 * Form that is nested below a component with a compound model containing a Hibernate
071 * model.
072 * @param id
073 */
074 public DataForm(String id) {
075 super(id);
076 }
077
078 /**
079 * @param key for the Hibernate session factory to be used with this component
080 * @return this
081 */
082 @Override
083 public DataForm setFactoryKey(Object key) {
084 super.setFactoryKey(key);
085 getPersistentObjectModel().setFactoryKey(key);
086 return this;
087 }
088
089 public HibernateObjectModel getPersistentObjectModel() {
090 return (HibernateObjectModel) getCompoundModel().getChainedModel();
091 }
092
093 /**
094 * Change the persistent model object of this form.
095 * @param object to attach to this form
096 * @return this form, for chaining
097 */
098 public DataForm setPersistentObject(Object object) {
099 getPersistentObjectModel().setObject(object);
100 modelChanged();
101 return this;
102 }
103
104 private void updateVersion() {
105 version = getPersistentObjectModel().getVersion();
106 }
107
108 /** Late-init version record. */
109 @Override
110 protected void onBeforeRender() {
111 super.onBeforeRender();
112 if (version == null)
113 updateVersion();
114 }
115
116 @Override
117 protected void onModelChanged() {
118 updateVersion();
119 }
120
121 /**
122 * Replaces the form's model object with a new, blank instance. Does not affect
123 * persistent storage.
124 * @return this form, for chaining
125 */
126 public DataForm clearPersistentObject() {
127 getPersistentObjectModel().unbind();
128 modelChanged();
129 return this;
130 }
131
132 protected CompoundPropertyModel getCompoundModel() {
133 IModel model = getModel();
134 Component cur = this;
135 while (cur != null) {
136 model = cur.getModel();
137 if (model != null && model instanceof CompoundPropertyModel)
138 return (CompoundPropertyModel) model;
139 cur = cur.getParent();
140 }
141 throw new WicketRuntimeException("DataForm has no parent compound model");
142 }
143
144 /**
145 * Saves the form's model object to persistent storage if it is new and commits
146 * the database transaction.
147 */
148 @Override
149 protected void onSubmit() {
150 Object modelObject = getPersistentObjectModel().getObject();
151 Session session = getHibernateSession();
152 if (!session.contains(modelObject)) {
153 session.save(modelObject);
154 // updating binding status; though it will happen on detach
155 // some UI components may like to know sooner.
156 getPersistentObjectModel().checkBinding();
157 }
158 super.onSubmit(); // flush and commit session
159 // if version is present it should have changed
160 if (version != null) {
161 updateVersion();
162 }
163 }
164
165 /**
166 * Checks that the version number, if present, is the last known version number.
167 * If it does not match, validation fails and will continue to fail until the form is
168 * reloaded with the updated data and version number. This allows the user to
169 * preserve her unsaved changes while preventing overwrites. <p> <b>Note:</b> although
170 * timestamp versions are supported, beware of rounding errors. equals() must return true
171 * when comparing the retained version object to the one loaded from persistent storage.
172 */
173 @Override
174 protected void validate() {
175 if (version != null) {
176 Serializable currentVersion = getPersistentObjectModel().getVersion();
177 if (!version.equals(currentVersion))
178 error(getString("version.mismatch", null)); // report error
179 // do not update version number as old data still appears in form
180 }
181
182 super.validate();
183 }
184
185 /**
186 * @return persistent storage version number if available, null otherwise
187 */
188 protected Serializable getVersion() {
189 return version;
190 }
191
192 /**
193 * Deletes the form's model object from persistent storage. Flushes change so that
194 * queries executed in the same request (e.g., in a HibernateListModel) will not return
195 * this object.
196 * @return true if the object was deleted, false if it did not exist
197 */
198 protected boolean deletePersistentObject() {
199 Session session = getHibernateSession();
200 Object modelObject = getPersistentObjectModel().getObject();
201 if (!session.contains(modelObject))
202 return false;
203 session.delete(modelObject);
204 session.flush();
205 return true;
206 }
207
208 public class ClearLink extends Link {
209 public ClearLink(String id) {
210 super(id);
211 }
212 @Override
213 public boolean isEnabled() {
214 return !DataForm.this.isVisibleInHierarchy() || getPersistentObjectModel().isBound();
215 }
216 @Override
217 public void onClick() {
218 clearPersistentObject();
219 DataForm.this.setVisible(true);
220 }
221 }
222 }